ELF x64 - Nanomites - Introduction_WP

ELF x64 - Nanomites - Introduction_WP

这是运用了Debug Blocker技术的题目
又花了些时间,终于破解了

gdb 设置跟踪父进程还是子进程

set follow-fork-mode parrent|child
当发生fork时,指示调试器执行父进程还是子进程

ptrace

在程序执行到int3时,就会触发 SIGTRAP 信号,

 long ptrace(enum __ptrace_request request,
             pid_t pid,
             void *addr,
             void *data);

参数data:作用则根据request的不同而变化,如果需要向目标进程中写入数据,data存放的是需要写入的数据;如果从目标进程中读数据,data将存放返回的数据

strace

可以先用strace来观察在系统调用
strace -i ./ch28.bin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[00007fa5f9b6bf4e] read(0, toto  /输入值
"toto\n", 1024) = 5
[00007fa5f9b75c22] mmap(NULL, 321, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa5f9c8e000
[00007fa5f9b4857b] clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fa5f9c43810) = 131358
[00007fa5f9b48286] wait4(131358, [{WIFSTOPPED(s) && WSTOPSIG(s) == SIGTRAP}], 0, NULL) = 131358
[00007fa5f9b48286] --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_TRAPPED, si_pid=131358, si_uid=1000, si_status=SIGTRAP, si_utime=0, si_stime=0} ---
[00007fa5f9b7329a] ptrace(PTRACE_GETREGS, 131358, NULL, 0x7fff0dde4900) = 0
[00007fa5f9b7329a] ptrace(PTRACE_SETREGS, 131358, NULL, 0x7fff0dde4900) = 0
[00007fa5f9b7329a] ptrace(PTRACE_CONT, 131358, NULL, 0) = 0
[00007fa5f9b48286] wait4(131358, [{WIFSTOPPED(s) && WSTOPSIG(s) == SIGTRAP}], 0, NULL) = 131358
[00007fa5f9b48286] --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_TRAPPED, si_pid=131358, si_uid=1000, si_status=SIGTRAP, si_utime=0, si_stime=0} ---
[00007fa5f9b7329a] ptrace(PTRACE_GETREGS, 131358, NULL, 0x7fff0dde4900) = 0
[00007fa5f9b7329a] ptrace(PTRACE_SETREGS, 131358, NULL, 0x7fff0dde4900) = 0
[00007fa5f9b7329a] ptrace(PTRACE_CONT, 131358, NULL, 0) = 0
[00007fa5f9b48286] wait4(131358, [{WIFEXITED(s) && WEXITSTATUS(s) == 1}], 0, NULL) = 131358
[00007fa5f9b48286] --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=131358, si_uid=1000, si_status=1, si_utime=0, si_stime=0} ---
[00007fa5f9b48286] wait4(131358, 0x7fff0dde4a1c, 0, NULL) = -1 ECHILD (No child processes)
[00007fa5f9b6bff3] write(1, "Wrong! try hard! :)\n", 20Wrong! try hard! :)
) = 20
[00007fa5f9b6c087] lseek(0, -1, SEEK_CUR) = -1 ESPIPE (Illegal seek)
[00007fa5f9b48759] exit_group(0) = ?
[????????????????] +++ exited with 0 +++

当我输入值‘toto’,触发了两次 SIGTRAP,之后退出

反编译

直接IDA PRO反编译
其他的细节都不管,就看我们需要的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
int __fastcall sub_400871(__int64 input)
{
int result; // eax
int stat_loc; // [rsp+1Ch] [rbp-F4h]
void *v3; // [rsp+F8h] [rbp-18h]
__pid_t pid; // [rsp+104h] [rbp-Ch]
void *dest; // [rsp+108h] [rbp-8h]

stat_loc = 0;
dest = mmap(0LL, 0x141uLL, 7, 34, -1, 0LL); // 0x7ffff7ffb000
memcpy(dest, src, 0x8DuLL); // src == 0x601080 指向400ac0( 0x48
pid = fork();
if ( !pid ) // 子进程
{
if ( ptrace(0, 0LL, 0LL, 0LL) == -1 )
{
puts("So you want to trace me?!");
exit(42);
}
v3 = dest;
((void (__fastcall *)(__int64))dest)(input);
exit(0);
}
while ( waitpid(pid, &stat_loc, 0) != -1 )
{
if ( (unsigned __int8)stat_loc == 127 )
{
if ( BYTE1(stat_loc) == 5 ) // 0x57f >>8 =0x5
sub_400736((__int64)dest, pid);
ptrace(PTRACE_CONT, (unsigned int)pid, 0LL, 0LL); //表示继续执行子进程
}
}
if ( BYTE1(stat_loc) )
result = puts("Wrong! try hard! :)");
else
result = puts("POOOOOOOOOOOOOOOOOOOOOOOOO God damn!! You won!");
return result;
}

子进程会执行 memcpy()h函数复制到dest的代码,src == 0x601080 指向400ac0,所以实际复制的是400ac0处的指令。

BUFlQK.jpg

RDI 保存着我们输入的数据,每次读取一个字节,存入al,之后int3,就会触发SIGTRAP,父进程进行调试子进程。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
__int64 __fastcall sub_400736(__int64 dest, unsigned int pid)
{
__int64 result; // rax
char v3; // [rsp+10h] [rbp-F0h] 0x7fffffffdc00 ==RAX 子进程寄存器值放在这里
__int64 v4; // [rsp+60h] [rbp-A0h]
__int64 v5; // [rsp+90h] [rbp-70h]
__int64 v6; // [rsp+A0h] [rbp-60h] 0x400a30 有问题
__int64 dest_1; // [rsp+F0h] [rbp-10h]
int i; // [rsp+F8h] [rbp-8h]
unsigned __int8 v9; // [rsp+FFh] [rbp-1h]
// 0x20cf9 子进程pid
v9 = 0;
dest_1 = dest + 1; // dest + 1
result = ptrace(PTRACE_GETREGS, pid, 0LL, &v3);// 获取值所有寄存器值??
//
for ( i = 0; i <= 12; ++i ) // 13个int 3
{ // rbp-0x8 = i
result = seg_6010A0[3 * i];
if ( v5 - dest_1 == result ) // v5 = 0x7ffff7ffb00c
// c-1 = b ==11
{
result = (unsigned int)v9++ + 1;
if ( v9 )
{
if ( seg_6010A0[3 * i + 1 + i % 2] == v4 )// v4 == input 表示输入值放在子进程r9中 0x7fffffffdc50
v6 |= 0x40uLL; // 修改了V6 0x7fffffffdc90 第十八个 表示EFLAGS
// 之前 rax 处 程序 JE zf=0 才不跳转 如果跳转则程序结束
result = ptrace(PTRACE_SETREGS, pid, 0LL, &v3);// 设置值
break;
}
}
}
if ( !v9 )
{
puts("Hummmmmmm NO WAY.");
exit(42);
}
return result;
}

ptrace(PTRACE_GETREGS, pid, 0LL, &v3)获取所有子进程寄存器值,存放在V3地址处。

if ( seg_6010A0[3 * i + 1 + i % 2] == v4 )// v4 == input 表示输入值放在子进程r9中 0x7fffffffdc50
这条语句就是我们需要的,其将我们的输入值与6010A0处的值进行比较,如果相等,则修改V6,也就是EFLAGS,将ZF置1JNE不跳转,如果跳转了直接结束进程。

ptrace(PTRACE_SETREGS, pid, 0LL, &v3)将修改后的寄存器值重新赋给子进程


if知道,比较共进行13次,下面编写python脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
data=[]
flag=[]
print("give me a bottle of rum!")

with open('ch28.txt','rt') as f:
for line in f.readlines():
if(line.find('db')!=-1):
pos=line.find('db')
item=line[pos+4:pos+6]
data.append(int(item,16))

for i in data:
print(format(i,'x'))

for i in range(13):
flag.append(data[3*i+1+i%2])
print('1111111111111111111111111111')
for i in flag:
print(chr(i),end='')

print()
print(len(flag))

ch28.txt保存6010A0开始的数据。

BUkHu8.jpg
运行得到flag

make -n 显示命令,但不执行


大佬代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import gdb
import re

password = "start_with_anything"
gdb.execute('file ./ch28.bin')
gdb.execute('set pagination off')
gdb.execute('br *0x40080d')
gdb.execute('run <<< '+password)
rdx = gdb.execute('info reg rdx',to_string=True)
rdx = re.findall('^rdx\s+\w+\s+(\d+)$', rdx)

password = "" + chr( int(rdx[0]) )
while True:
gdb.execute('run <<< '+password)
# Continue to last character
for i in password:
gdb.execute('c')
try:
rdx = gdb.execute('info reg rdx',to_string=True)
except gdb.error:
# If info reg fails it is probably because the program has exited.
break
rdx = re.findall('^rdx\s+\w+\s+(\d+)$', rdx)
password += chr( int(rdx[0]) )

print("Password found is :"+password)

gdb.execute('quit')

论文 想法

云攻击

让VM支持去重,然后flush+reload

建立cpu cache的随机策略
映射