-
-
[原创]KCTF 2021秋季赛 第七题 声名远扬
-
2021-12-3 11:54 18326
-
很久没在看雪写东西了,题目本身不难,晚上抽了点时间看看,最后想的是尽量用工具去做,第一次写WP,简单记录下。
起点
- 32位,无保护,ida搜索字符串没有看到错误输出,没有明显的函数处理过程
- 发现Duilib写的,下载源码,编译lib,https://github.com/duilib/duilib,pct,sigmake制作签名文件,应用之~
- 识别了500多个还可以了,阅读源码,跟踪按钮事件,CNotifyPump::NotifyPump->CNotifyPump::LoopDispatch,进而跟踪到按钮消息处理函数sub_89D2D0。
- 分析出大致流程如下:1234567891011121314
int
__userpurge sub_89D2D0@<eax>(
int
a1@<ecx>,
int
a2@<ebx>,
int
a3@<edi>,
int
a4@<esi>,
int
a5)
{
v13
=
a1;
result
=
a1;
if
(
*
(_DWORD
*
)(a1
+
1384
)
=
=
*
(_DWORD
*
)(a5
+
136
) )
{
/
/
...
v10
=
sub_89E530(a1a, a2a);
/
/
编码函数
/
/
...
sub_89D600(&v15, a2, a3, a4, v7, (
int
)v16);
/
/
校验函数
(
*
(void (__thiscall
*
*
)(_DWORD, char
*
))(
*
*
(_DWORD
*
*
)(v13
+
1388
)
+
44
))(
*
(_DWORD
*
)(v13
+
1388
), v16);
/
/
结果提示
}
return
result;
}
分析
- 关键代码1(编码函数),一个简单的类似base64的编码,这里我想的是直接通过ida脚本去把表提出来。123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
for
( i
=
0
; ; i
+
=
v11 )
{
v2
=
get_len(a2);
v11
=
v2
-
i;
v27
=
'@'
;
Src
=
*
(_BYTE
*
)sub_89E970(v30, (
int
)&v27);
/
/
从编码表中替换
v26
=
64
;
v32
=
*
(_BYTE
*
)sub_89E970(v30, (
int
)&v26);
v25
=
64
;
v33
=
*
(_BYTE
*
)sub_89E970(v30, (
int
)&v25);
v24
=
0x40
;
v34
=
*
(_BYTE
*
)sub_89E970(v30, (
int
)&v24);
if
( !v11 )
break
;
if
( v11
=
=
1
)
{
v23
=
((
int
)
*
(unsigned __int8
*
)char_at(i) >>
2
) &
0x3F
;
/
/
char_at是std::string.at
Src
=
*
(_BYTE
*
)sub_89E970(v30, (
int
)&v23);
v22
=
(
16
*
*
(_BYTE
*
)char_at(i)) &
0x3F
;
v32
=
*
(_BYTE
*
)sub_89E970(v30, (
int
)&v22);
v21
=
64
;
v33
=
*
(_BYTE
*
)sub_89E970(v30, (
int
)&v21);
v20
=
64
;
v34
=
*
(_BYTE
*
)sub_89E970(v30, (
int
)&v20);
}
else
if
( v11
=
=
2
)
{
v19
=
((
int
)
*
(unsigned __int8
*
)char_at(i) >>
2
) &
0x3F
;
Src
=
*
(_BYTE
*
)sub_89E970(v30, (
int
)&v19);
v3
=
16
*
*
(_BYTE
*
)char_at(i);
v18
=
(((
int
)
*
(unsigned __int8
*
)char_at(i
+
1
) >>
4
) | v3) &
0x3F
;
v32
=
*
(_BYTE
*
)sub_89E970(v30, (
int
)&v18);
v17
=
(
4
*
*
(_BYTE
*
)char_at(i
+
1
)) &
0x3F
;
v33
=
*
(_BYTE
*
)sub_89E970(v30, (
int
)&v17);
v16
=
64
;
v34
=
*
(_BYTE
*
)sub_89E970(v30, (
int
)&v16);
}
else
{
v15
=
((
int
)
*
(unsigned __int8
*
)char_at(i) >>
2
) &
0x3F
;
Src
=
*
(_BYTE
*
)sub_89E970(v30, (
int
)&v15);
v4
=
(
16
*
*
(_BYTE
*
)char_at(i)) &
0x3F
;
v14
=
(((
int
)
*
(unsigned __int8
*
)char_at(i
+
1
) >>
4
) &
0x3F
| v4) &
0x3F
;
v32
=
*
(_BYTE
*
)sub_89E970(v30, (
int
)&v14);
v5
=
4
*
*
(_BYTE
*
)char_at(i
+
1
);
v13
=
(((
int
)
*
(unsigned __int8
*
)char_at(i
+
2
) >>
6
) | v5) &
0x3F
;
v33
=
*
(_BYTE
*
)sub_89E970(v30, (
int
)&v13);
v12
=
*
(_BYTE
*
)char_at(i
+
2
) &
0x3F
;
v34
=
*
(_BYTE
*
)sub_89E970(v30, (
int
)&v12);
v11
=
3
;
}
- 写个调试脚本如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | / / 大致流程是这样,不过调试的时候连续执行step_over我再ida里一直没成功,没时间看了,有知道的兄弟请告知. / / 因为本身循环次数不多,通过条件断点输出和多次run脚本把表打了出来 import time for i in range ( 64 ): edx = idc.get_reg_value( "EBP" ) - 0x39 idc.patch_dbg_byte(edx,i) idc.set_reg_value( 0x0089E5AF , 'EIP' ) idaapi.step_over() idaapi.step_over() idaapi.step_over() idaapi.step_over() time.sleep( 0.1 ) eax = idc.get_reg_value( "EAX" ) code = idc.read_dbg_byte(eax) print ( hex (code), end = '') / / 编码表[ 0x70 , 0x72 , 0x76 , 0x6f , 0x39 , 0x43 , 0x48 , 0x53 , 0x4a , 0x4f , 0x63 , 0x50 , 0x49 , 0x62 , 0x36 , 0x78 , 0x52 , 0x56 , 0x55 , 0x58 , 0x51 , 0x7a , 0x30 , 0x71 , 0x42 , 0x47 , 0x44 , 0x45 , 0x37 , 0x32 , 0x4c , 0x4e , 0x5a , 0x64 , 0x75 , 0x61 , 0x65 , 0x66 , 0x59 , 0x54 , 0x35 , 0x4b , 0x5f , 0x38 , 0x2d , 0x34 , 0x46 , 0x41 , 0x68 , 0x6c , 0x69 , 0x6d , 0x6a , 0x6b , 0x6e , 0x67 , 0x74 , 0x31 , 0x79 , 0x4d , 0x57 , 0x73 , 0x33 , 0x77 , 0x21 ] |
- 关键代码2(校验函数),0089D8A2:call fword ptr [ebp+var_C] 校验代码放在了x64里执行,所以直接上windbg
- 看了看代码,一个简单的比较,bp 00895606 下断点,da rbp-30打印输出,直接拿最后的编码比较
- GYldGg-iIoJlPX9hPXpjPqfdEY21B01TBTzeGqfKNR!!
求解
- 写了个简单的z3求解
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 | from z3 import * table = [ 0x70 , 0x72 , 0x76 , 0x6f , 0x39 , 0x43 , 0x48 , 0x53 , 0x4a , 0x4f , 0x63 , 0x50 , 0x49 , 0x62 , 0x36 , 0x78 , 0x52 , 0x56 , 0x55 , 0x58 , 0x51 , 0x7a , 0x30 , 0x71 , 0x42 , 0x47 , 0x44 , 0x45 , 0x37 , 0x32 , 0x4c , 0x4e , 0x5a , 0x64 , 0x75 , 0x61 , 0x65 , 0x66 , 0x59 , 0x54 , 0x35 , 0x4b , 0x5f , 0x38 , 0x2d , 0x34 , 0x46 , 0x41 , 0x68 , 0x6c , 0x69 , 0x6d , 0x6a , 0x6b , 0x6e , 0x67 , 0x74 , 0x31 , 0x79 , 0x4d , 0x57 , 0x73 , 0x33 , 0x77 , 0x21 ] A = Array( 'A' , IntSort(), IntSort()) i = 0 for elem in table: A = Store(A, i, elem) i = i + 1 permute = lambda k:Select(A, BV2Int(k)) flag_len = 31 x = [BitVec( 'u%d' % i, 8 ) for i in range (x_len)] s = Solver() i = 0 v = 0 xxx = [] while True : k = x_len - i if (k = = 0 ): break if k = = 1 : pos = (x[i] >> 2 ) & 0x3f m0 = permute(pos) pos = ( 16 * x[i]) & 0x3f m1 = permute(pos) pos = BitVecVal( 0x40 , 8 ) m2 = permute(pos) m3 = permute(pos) elif k = = 2 : pos = (x[i] >> 2 ) & 0x3f m0 = permute(pos) pos = ((x[i + 1 ] >> 4 ) | ( 16 * x[i])) & 0x3f m1 = permute(pos) pos = ( 4 * x[i + 1 ]) & 0x3f m2 = permute(pos) pos = BitVecVal( 0x40 , 8 ) m3 = permute(pos) else : pos = (x[i] >> 2 ) & 0x3f m0 = permute(pos) pos = ((x[i + 1 ] >> 4 ) & 0x3f | ( 16 * x[i])) & 0x3f m1 = permute(pos) pos = ((x[i + 2 ] >> 6 ) | ( 4 * x[i + 1 ])) & 0x3f m2 = permute(pos) pos = x[i + 2 ] & 0x3f m3 = permute(pos) k = 3 xxx + = [m0,m1,m2,m3] i + = k cip = 'GYldGg-iIoJlPX9hPXpjPqfdEY21B01TBTzeGqfKNR!!' j = 0 for t in xxx: s.add(t = = ord (cip[j])) j + = 1 res = s.check() if res = = z3.sat: print (s.model()) model = s.model() flag = '' for i in range ( 0 ,flag_len): flag + = chr (model[x[i]].as_long().real) print (flag) else : print ( 'oh,no' ) |
- 最后得到:flag{2021-10-04-yangyangbudeyi}
赞赏
他的文章