2022 美团MTCTF Re wp
小A一个
pwn爷爷带到第一名了
small
看汇编即可
通过
mov rax 1
call syscall
来实现exit等各种系统调用
loc_10068: ; CODE XREF: LOAD:0000000000010062↑j
LOAD:0000000000010068 add ebx, 67452301h ; Add
LOAD:000000000001006E shl ecx, 4 ; Shift Logical Left
LOAD:0000000000010071 inc ecx ; Increment by 1
LOAD:0000000000010073 shr eax, 5 ; Shift Logical Right
LOAD:0000000000010076 add eax, 23h ; '#' ; Add
LOAD:0000000000010079 xor ecx, eax ; Logical Exclusive OR
LOAD:000000000001007B mov eax, edi
LOAD:000000000001007D add eax, ebx ; Add
LOAD:000000000001007F xor ecx, eax ; Logical Exclusive OR
LOAD:0000000000010081 add ebp, ecx ; Add
LOAD:0000000000010083 mov ecx, ebp
LOAD:0000000000010085 shr ecx, 5 ; Shift Logical Right
LOAD:0000000000010088 add ecx, 67h ; 'g' ; Add
LOAD:000000000001008B mov eax, ebp
LOAD:000000000001008D shl eax, 4 ; Shift Logical Left
LOAD:0000000000010090 add eax, 45h ; 'E' ; Add
LOAD:0000000000010093 xor ecx, eax ; Logical Exclusive OR
LOAD:0000000000010095 mov eax, ebp
LOAD:0000000000010097 add eax, ebx ; Add
LOAD:0000000000010099 xor ecx, eax ; Logical Exclusive OR
LOAD:000000000001009B add edi, ecx ; Add
LOAD:000000000001009D inc edx ; Increment by 1
LOAD:000000000001009F cmp edx, 35 ; Compare Two Operands
LOAD:00000000000100A2 jl short loc_10052 ; Jump if Less (SF!=OF)
LOAD:00000000000100A4 mov [rsi-8], ebp
LOAD:00000000000100A7 mov [rsi-4], edi
LOAD:00000000000100AA mov ecx, [rsp+10h]
LOAD:00000000000100AE add ecx, 20h ; ' ' ; Add
LOAD:00000000000100B1 cmp esi, ecx ; Compare Two Operands
LOAD:00000000000100B3 jl loc_1000B ; Jump if Less (SF!=OF)
LOAD:00000000000100B9 xchg rax, rsi ; Exchange Register/Memory with Register
LOAD:00000000000100BB mov esi, 20h ; ' '
LOAD:00000000000100C0
LOAD:00000000000100C0 loc_100C0: ; CODE XREF: LOAD:00000000000100D3↓j
LOAD:00000000000100C0 mov cl, [rax]
LOAD:00000000000100C2 cmp byte ptr dword_100F7[esi], cl ; Compare Two Operands
LOAD:00000000000100C9 jnz short exit_ ; Jump if Not Zero (ZF=0)
LOAD:00000000000100CB dec esi ; Decrement by 1
LOAD:00000000000100CD dec rax ; Decrement by 1
LOAD:00000000000100D0 cmp esi, 0 ; Compare Two Operands
LOAD:00000000000100D3 jnz short loc_100C0 ; Jump if Not Zero (ZF=0)
LOAD:00000000000100D5 mov edi, 1
LOAD:00000000000100DA mov esi, 100F3h
LOAD:00000000000100DF mov eax, 1
LOAD:00000000000100E4 mov dx, 4
LOAD:00000000000100E8 syscall ; LINUX -
LOAD:00000000000100EA
LOAD:00000000000100EA exit_: ; CODE XREF: LOAD:000000000001002C↑j
LOAD:00000000000100EA ; LOAD:00000000000100C9↑j
LOAD:00000000000100EA mov eax, 60
LOAD:00000000000100EF mov edi, esi
LOAD:00000000000100F1 syscall ; LINUX -
tea实现
魔改了轮数 35
delta 0x67452301
改改tea脚本即可
#include <stdio.h>
#include <stdlib.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
void Decrypt(unsigned long *v, unsigned long *k)
{
unsigned long n = 35, sum, v0 = v[0], v1 = v[1];
/*初始化*/
unsigned long delta = 0x67452301; /* 密钥调度常数*/
sum = delta*n;
/*即0xC6EF3720 */
while (n-- > 0)
{
/*基本循环开始*/
v1 -= ((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3]);
v0 -= ((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1]);
sum -= delta;
}
/*循环结束*/
v[0] = v0;
v[1] = v1;
}
int main()
{
unsigned long v[9]={0x0DE087143,0x0C4F91BD2,0x0DAF6DADC,0x6D9ED54C,0x75EB4EE7,0x5D1DDC04,0x511B0FD9,0x51DC88FB,0};
unsigned long k[4]={1,0x23,0x45,0x67};
Decrypt(v,k);
Decrypt(v+2,k);
Decrypt(v+4,k);
Decrypt(v+6,k);
puts((char*)v);
}
static
评价是想多了
用原版aes 和 原版xxtea
main 除了检测flag格式以外的逻辑基本都是假的
下断点发现断不下来就能发现这些逻辑都跑不到
findcrypt 查到aes表
向上x找到
sub_4064F0 函数 为主要加密逻辑
再向上x可以找到check逻辑
在0000000405267 的cmp check
在 loc_4051F6 赋值enc
enc即为 dword_14640D0
动调
构建flag{00000000000000000000000000000000}
发现是分成两个16byte加密的
前半段为sub4062b0 负责加密
是aes查表法的实现
根据aes原理 第一次异或的16bytes就是密钥key( 记得改端序 卡了我好几个小时)
EB9E779E809831361C2764A239BA3A0B改端序为
0B3ABA39A264271C363198809E779EEB
cyberchef 使用ecb/nopadding
然后hextostr即可
得到前半段flag
2e64949cd16c4449
接着分析sub_4064F0 后面的函数
在sub_12531C8等函数中看到字符串"/root/code/unicorn/qemu/util/cacheinfo.c"
有unicorn字样
想起上次某ctf的模拟执行有很多个架构)
就一个长数组byte_19762C0 很明显是asm
dump到010 再拖入ida 选择arm little 64
直接c f5
unsigned __int8 *__fastcall sub_0(unsigned __int8 *result)
{
int *v0; // x9
int v2; // w8
int *v3; // x9
int v4; // w8
int j; // [xsp+0h] [xbp-50h]
int v6; // [xsp+8h] [xbp-48h]
unsigned int i; // [xsp+Ch] [xbp-44h]
unsigned int sum; // [xsp+10h] [xbp-40h]
unsigned int v9; // [xsp+14h] [xbp-3Ch]
unsigned int v1; // [xsp+18h] [xbp-38h]
int k[4]; // [xsp+1Ch] [xbp-34h]
int v12; // [xsp+2Ch] [xbp-24h]
_DWORD *v13; // [xsp+30h] [xbp-20h]
char v14[16]; // [xsp+38h] [xbp-18h] BYREF
unsigned __int8 *v15; // [xsp+48h] [xbp-8h]
v15 = result;
v14[0] = *result ^ result[5] ^ (32 * result[2]) ^ (2 * result[9]) ^ (2 * result[10]);
v14[1] = result[1] ^ (result[13] >> 2) ^ (result[12] >> 2) ^ result[15] ^ (result[4] >> 6);
v14[2] = result[2] ^ (result[1] << 7) ^ (result[15] >> 6) ^ (8 * result[14]) ^ (result[4] >> 1);
v14[3] = result[3] ^ (2 * result[10]) ^ (result[14] >> 4) ^ (result[6] >> 4) ^ (32 * result[13]);
v14[4] = result[4] ^ (4 * result[3]) ^ result[10] ^ (2 * *result) ^ (result[1] >> 2);
v14[5] = result[5] ^ (result[1] >> 3) ^ (result[13] << 7) ^ (result[2] >> 7) ^ (4 * result[8]);
v14[6] = result[6] ^ (result[8] >> 7) ^ (4 * result[5]) ^ (16 * result[3]) ^ (result[14] >> 3);
v14[7] = result[7] ^ (result[11] >> 6) ^ (result[2] >> 5) ^ (result[3] << 6) ^ (2 * result[1]);
v14[8] = result[8] ^ (result[11] << 7) ^ (result[5] >> 6) ^ (2 * result[4]) ^ (16 * result[6]);
v14[9] = result[9] ^ (8 * result[15]) ^ (result[4] >> 3) ^ (32 * result[12]) ^ result[2];
v14[10] = result[10] ^ (*result >> 2) ^ (2 * result[9]) ^ (result[5] << 7) ^ (result[11] >> 7);
v14[11] = result[11] ^ result[5] ^ (result[10] >> 4) ^ (result[6] >> 6) ^ (result[3] >> 6);
v14[12] = result[12] ^ (result[4] << 6) ^ (result[2] >> 1) ^ (result[15] >> 1) ^ (result[11] << 7);
v14[13] = result[13] ^ (result[6] >> 3) ^ (result[9] >> 7) ^ (32 * result[1]) ^ (result[11] >> 7);
v14[14] = result[14] ^ (result[7] << 7) ^ (16 * result[9]) ^ (result[8] >> 1) ^ (16 * result[2]);
v14[15] = result[15] ^ (*result >> 1) ^ (result[13] >> 6) ^ (4 * result[7]) ^ (result[11] >> 5);
v13 = v14;
v12 = 4;
k[0] = 12;
k[1] = 34;
k[2] = 56;
k[3] = 78;
v6 = 19;
sum = 0;
v9 = *&v14[12];
do
{
sum -= 0x21524111;
for ( i = 0; i < v12 - 1; ++i )
{
v1 = v13[i + 1];
v0 = &v13[i];
v2 = *v0 + ((((4 * v1) ^ (v9 >> 5)) + ((16 * v9) ^ (v1 >> 3))) ^ ((sum ^ v1) + (k[(i ^ (sum >> 2)) & 3] ^ v9)));
*v0 = v2;
v9 = v2;
}
v3 = &v13[v12 - 1];
v4 = *v3
+ ((((4 * *v13) ^ (v9 >> 5)) + ((16 * v9) ^ (*v13 >> 3))) ^ ((sum ^ *v13) + (k[(i ^ (sum >> 2)) & 3] ^ v9)));
*v3 = v4;
v9 = v4;
--v6;
}
while ( v6 );
for ( j = 0; j < 16; ++j )
v15[j] = v14[j];
return result;
}
分析发现是xxtea 用xxtea脚本解密即可
#include <stdint.h>
#include <stdio.h>
using namespace std;
#define DELTA -0x21524111
#define MX (((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((sum ^ y) + (key[(p & 3) ^ e] ^ z)))
void xxtea(uint32_t *v, int n, uint32_t *key)
{
uint32_t y, z;
int sum;
unsigned p, rounds, e;
// encrypt
if (n > 1)
{
rounds = 6 + 52 / n;
sum = 0;
z = v[n - 1];
do
{
sum += DELTA;
e = (sum >> 2) & 3;
for (p = 0; p < n - 1; p++)
{
y = v[p + 1];
z = v[p] += MX;
}
y = v[0];
z = v[n - 1] += MX;
} while (--rounds);
}
// decrypt
else if (n < -1)
{
n = -n;
rounds = 6 + 52 / n;
sum = rounds * DELTA;
y = v[0];
do
{
e = (sum >> 2) & 3;
for (p = n - 1; p > 0; p--)
{
z = v[p - 1];
y = v[p] -= MX;
}
z = v[n - 1];
y = v[0] -= MX;
sum -= DELTA;
} while (--rounds);
}
}
int main()
{
uint32_t v[5] = {0xFB732728, 0x4D26D4BB, 0xDA122E3A, 0x26BAFC68};
uint32_t k[4] = {12, 34, 56, 78};
xxtea(v, -4, k);
for (int i = 0; i < 4; i++)
{
printf("%x ", v[i]);
}
}
得到9a1b0035 8d92ebb1 7de7f82 a497d0b4 转端序
再用z3解前面的混淆即可
import re
from z3 import *
result = [BitVec(f"result[{i}]", 8) for i in range(16)]
v14 = [0] * 16
v14[0] = result[0] ^ result[5] ^ (32 * result[2]) ^ (2 * result[9]) ^ (2 * result[10])
v14[1] = result[1] ^ (result[13] >> 2) ^ (result[12] >> 2) ^ result[15] ^ (result[4] >> 6)
v14[2] = result[2] ^ (result[1] << 7) ^ (result[15] >> 6) ^ (8 * result[14]) ^ (result[4] >> 1)
v14[3] = result[3] ^ (2 * result[10]) ^ (result[14] >> 4) ^ (result[6] >> 4) ^ (32 * result[13])
v14[4] = result[4] ^ (4 * result[3]) ^ result[10] ^ (2 * result[0]) ^ (result[1] >> 2)
v14[5] = result[5] ^ (result[1] >> 3) ^ (result[13] << 7) ^ (result[2] >> 7) ^ (4 * result[8])
v14[6] = result[6] ^ (result[8] >> 7) ^ (4 * result[5]) ^ (16 * result[3]) ^ (result[14] >> 3)
v14[7] = result[7] ^ (result[11] >> 6) ^ (result[2] >> 5) ^ (result[3] << 6) ^ (2 * result[1])
v14[8] = result[8] ^ (result[11] << 7) ^ (result[5] >> 6) ^ (2 * result[4]) ^ (16 * result[6])
v14[9] = result[9] ^ (8 * result[15]) ^ (result[4] >> 3) ^ (32 * result[12]) ^ result[2]
v14[10] = result[10] ^ (result[0] >> 2) ^ (2 * result[9]) ^ (result[5] << 7) ^ (result[11] >> 7)
v14[11] = result[11] ^ result[5] ^ (result[10] >> 4) ^ (result[6] >> 6) ^ (result[3] >> 6)
v14[12] = result[12] ^ (result[4] << 6) ^ (result[2] >> 1) ^ (result[15] >> 1) ^ (result[11] << 7)
v14[13] = result[13] ^ (result[6] >> 3) ^ (result[9] >> 7) ^ (32 * result[1]) ^ (result[11] >> 7)
v14[14] = result[14] ^ (result[7] << 7) ^ (16 * result[9]) ^ (result[8] >> 1) ^ (16 * result[2])
v14[15] = result[15] ^ (result[0] >> 1) ^ (result[13] >> 6) ^ (4 * result[7]) ^ (result[11] >> 5)
t = "35001b9ab1eb928d827fde07b4d097a4"
t = re.findall(".{2}", t)
for index in range(len(t)):
t[index] = int(t[index], 16)
s = Solver()
for index in range(16):
s.add(v14[index] == t[index])
print(s.check())
f = s.model()
print(f)
result[5] = 97
result[14] = 54
result[1] = 55
result[2] = 51
result[10] = 52
result[8] = 99
result[4] = 48
result[0] = 56
result[15] = 55
result[11] = 101
result[7] = 99
result[13] = 54
result[3] = 50
result[9] = 50
result[6] = 48
result[12] = 54
for x in result:
print(chr(x),end="")
得到后半段flag
87320a0cc24e6667
拼起来改为uuid格式即可
flag{2e64949c-d16c-4449-8732-0a0cc24e6667}