失神我醉了 发表于 2024-5-2 15:43

flare-on ctf 2023 第三题mypassion分析

本帖最后由 失神我醉了 于 2024-5-2 22:19 编辑

前言
Flare-on每年的ctf都比较有趣,大家可以去试一下,已经办了10届比赛了,官网显示这届最难,因为庆祝比赛10周年。本文就2023的第三题mypassion学习一波逆向,很考验逆向基本功。主要防止忘了,写下来回顾一波。

题目:aHR0cHM6Ly9mbGFyZS1vbi5jb20v

分析

查壳,无壳。



```
strings.exe .\mypassion.exe 查看一下字符串,发现了有趣的字符串
C:\send\bitcoin\for\symbols\send\bitcoin\for\symbols\send\bitcoin\for\symbols\send\bitcoin\for\sym.pdb
```

挖矿的?可能根据某恶意软件修改的。运行了一下程序,后无反应,ok。

IDA分析

_start函数没有反调试,直接来到main函数,可以看到程序运行需要带参数,并且参数要满足一些条件,大致用ida看了一下程序执行流程,没有看到特别有用

的信息,不能一步登天。函数会在运行时会从数据段导shellcode执行,所以我打算动静结合,慢慢分析。首先,参数第7个字母影响VirtualAlloc,第一个字母为'0',   第2,3字母得满足x+4y == 0x127, 用py找一下, 我选择9,C

```
for x in range(0x20,0x7f):
      for y in range(0x20,0x7f):
                if(x+4*y==0x127):
                        print(chr(x),chr(y))
```



所以得到初步参数09CxxRxxxxxxxxx,   然后用windbg动态调试,windbgx.exe .\passion.exe 09CxxRxxxxxxxxx,最好不要用powershell,后面会出现奇怪的问题?导致程序运行失败。cmd没问题
然后我们在virtualAlloc下断点,程序在这儿退出了,看下原因



用err.exe查看错误值原因,查看相关文档,得到,virtualAlloc最后一个参数得是PAGE_EXECUTE_READWRITE(0x40)。即参数第7个字母得为@。



修改参数为09CxxR@xxxxxxxxx,继续往下分析



因为参数13字母会改代码,所以在func1函数入口下个断点,看看这个函数输入是什么,具体代码是干什么的
在偏移1299下断点,可以看到输入参数v39的值是5个'x', 为什么5个,因为v40和v39相邻,v40赋值为'x',高位被清零(汇编代码看会更明白),如图:



然后我们可以明显看到xabort56h这行汇编有问题,c678f456 (16进制) ,而78正好是'x'的16进制ASCII码,所以这个是受第13字母影响,我们可以猜测c6后面可能是45(试试),因为上面几行都

是,即字母'E'。所以新参数:09CxxR@xxxxxExxx。也可以用CyberChef进行调试汇编(慢慢猜。。),后面也会用到,因为好几个地方会通过参数影响shellcode。



那么这段汇编代码在干嘛呢?它好像对后续没什么影响?直接分析很痛苦,动态分析就很舒服,我们知道输入是5个'x',即参数8-12位置的值,我们直接在ret前f9下断点,看看输出,相关寄存器啥情况,可以发现rsp有东西'brUc3',正好5位,单步分析的话会发现rsp的字符是临时算出来的。猜测可能和这个字符串比较,暂时不知道有什么用,后面相邻的代码也没用到它,但是我们可以把x改为这个,反正没影响。



```
chr((0x74^0x16)) ='b'
chr((0x65^0x17)) ='r'
chr((0x6E^0x3B)) ='U'
chr((0x74^0x17)) ='c'
chr((0x65^0x56)) ='3'
```

所以新参数:09CxxR@brUc3Exxx

继续往下分析,结合chatgpt和分析,将main函数中后面的函数重命名



接着进入sub_1400013E0继续分析,ida静态分析一波,可以看到,函数会通过'/'来截取参数,所以修改新参数为:09CxxR@brUc3Exxx/test1/test2/ , 大体分析一下,sub_1400013E0会从数据段写入文件从而创建文件,并且会解密数据段的数据,作为代码执行。





sub_1400013E0中间还有个有趣的部分,现在用不到,但后面有影响,即v25变量,v25 = 0x1f + day(系统今天的日期),v25变量会和数据段进行运算(而这块正属于文件的尾部)。



继续后面,我们通过动态分析,进入sub_1400013E0动态解密的code,看看这块具体干了什么,在1892哪儿下断点,运行调试器后会发现生成了test1的文件,所以参数会影响文件名。
到达断点后,我们直接单步进入动态分析。在函数前下断点,我们只关心输入和输出。



根据main函数,可以得到390是retrieve_140002F00,380是strtol_1400071A0,358是ExitProcess,以此类推,最后的3B0是sub_1400018B0函数,所以后面需要分析sub_1400018B0
动态分析后,可以调试得到这段代码在截取test2,并且test2前面得含有数字,并且该数字会转为4为基数的数字,然后与后面数字后面的字符串长度比较,比如这里我选择0x20,转为4为基数为8,所以后面需要8个字符,不然ExitProcess,so修改参数为09CxxR@brUc3Exxx/test1/20testtest/

接着分析sub_1400018B0函数,大体过一遍,该函数会读取之前创建的test1文件,并通过输入参数对该文件进行一些判断。该函数开头和retrieve_140002F00作用一样,在这里它截取第三段,修改参数09CxxR@brUc3Exxx/test1/20testtest/test3/,然后在exitprocess函数下断点,查看一下条件满足





所以第三段得改为'?pizza',加‘?’是因为v8没有被比较,跟踪一下v12,v12的逻辑与之前sub_1400013E0函数中v25 变量一样,所以v8 =0x1f + day(今天30)即字符'='
修改参数为09CxxR@brUc3Exxx/test1/20testtest/=pizza/



接下来我们在下一个exitprocess下断点,可以看到判断逻辑是文件头,所以我们在sub_1400013E0创建文件时忽略了一些东西,我们再去看一下哪里对文件头进行了操作。



回到sub_1400013E0,文件开头第一段进行了操作,从中提取了数字,所以第一段得包含数字,赋值给了v5,而v5会影响文件头,所以修改参数为:09CxxR@brUc3Exxx/1337test1/20testtest/=pizza/



用010editor,可以看见test1开头就是0x11333377。继续sub_1400018B0函数,调试定位下一个exitprocess前面,看看需要什么条件。





调试可以得到,比较的是文件名,需要为pr.ost,所以修改参数为09CxxR@brUc3Exxx/1337pr.ost/20testtest/=pizza/,成功通过判断。后面函数进入sub_140002170。

sub_140002170里面的逻辑是比较难以理解的,开头的sub_140001DA0,sub_140001F80返回的都是函数指针,然后里面的逻辑也是动态生成的,不能理解。sub_140001F80还受输入参数影响,会改变代码

试试用windbg从内存打印sub_140001DA0函数指针的汇编代码,进行分析,或者动态分析函数输入输出从而了解功能。sub_140001F80后面再分析。





sub_140001DA0的f1.bin丢进ninja可以反汇编为c语言,该函数功能与retrieve_140002F00一样,用'/'截取字符串,也可以通过动态分析根据输入输出知晓功能。



shellcode2暂时理解不了,但他与shellcode3相关。但是我们可以猜想一下,输入的参数是个指针和字符串,输出是指针。那么就有这么几个情况,1.对字符串的处理,字符串对比或者添加什么的。2.看返回

值,shellcode2最后赋值给了*(a1+3C8),在后面的sub_140002910函数中,可以找到它被用做函数,所以返回的是函数指针,可以猜想shellcode2的作用可能是根据字符串查找函数地址,类似GetProcAddress

函数,shellcode2的输入指针还是shellcode3的返回值。理解shellcode2有助于理解shellcode3。先在shellcode3下断点,因为输入参数会影响shellcode3的代码,我们把汇编代码扣出来,慢慢分析。修改参数

为:09CxxR@brUc3Exxx/1337pr.ost/20testtest/=pizza/XXXXXXXXXXXXX/





将shellcode3抠出来的16进制,放到cyberchef,'X'的ASCII码是0x58,找到无效的指令,影响shellcode3代码的输入字符(第四段字符串),依次位置是“12,5,8,9,7,6”,然后仔细分析一下应该用什么代替





在上图,我们慢慢分析,首先第一个58 后面接着的是 “48 8b”,明显像mov指令,所以大概率是mov,那么什么指令后面还能接mov,而且"MOV RAX,QWORD PTR ",有个60,显而易见mov rax, qword ptr gs:,该指令作用是通过peb获取模块地址,二进制如下:"\x65\x48\x8B\x04\x25\x60\x00\x00\x00\",其实知道了第一个指令的值,后面就很好知道了,显然shellcode3是通过peb获取指定模块地址,然后shellcode2从指定模块中获取指定函数地址。那么哪个模块有beep函数,谷歌搜一下我们知道Kernel32.dll(分析汇编代码也可知)。所以shellcode3是为了获取Kernel32.dll模块地址。如果不熟悉,可以用搜索x64windows shellcode开发。后面的shellcode2修复也与之相关。修复后,汇编代码如下。



```
Source = 0x65 = e
Source   = 0x60 = `
Source   = 0x2e = .
Source   = 0x43 = C
Source   = 0x52 = R
Source   = 0x30 = 0
```

```
MOV RAX,QWORD PTR GS: ;PEB基址
MOV RCX,QWORD PTR ;PEB中PEB_LDR_DATA 结构

; 获取 PEB_LDR_DATA 中的 InMemoryOrderModuleList 的地址
MOV RDX,QWORD PTR

;获取LDR_DATA_TABLE_ENTRY 结构
SUB RDX,0000000000000010

; 判断模块名称
MOV RAX,QWORD PTR
CMP WORD PTR ,002E   ;'.'
JNE 000000000000004E
CMP WORD PTR ,0032   ;'2'
JNE 000000000000004E
CMP WORD PTR ,0033   ;'3'
JNE 000000000000004E
CMP WORD PTR ,004C   ;'L'
JE 000000000000003C
JS 0000000000000041
INS BYTE PTR ,DX
JNE 000000000000004E
MOVZX EAX,WORD PTR    ;'E'
MOV ECX,0000FFDF
SUB AX,0045   ;0x45='E'
TEST CX,AX
JE 0000000000000060
;循环 InMemoryOrderModuleList 的地址链表
MOV RAX,QWORD PTR
;获取LDR_DATA_TABLE_ENTRY 结构
SUB RDX,0000000000000010
;判断DllBase是否为空
CMP QWORD PTR ,0000000000000000
JNE 0000000000000015
XOR EAX,EAX
RET
;返回DllBase模块基址
MOV RAX,QWORD PTR
RET
```

修改参数为:09CxxR@brUc3Exxx/1337pr.ost/20testtest/=pizza/XXXXX`0R.CXXeXXXX/。接着我们修复shellcode2,它也被输入参数影响。一样的,导入cyberchef,慢慢分析。





shellcode2功能与GetProcAddress一样,一顿操作猛如虎后,可以得到输入参数满足以下时,shellcode2可以运行。修改参数为:09CxxR@brUc3Exxx/1337pr.ost/20testtest/=pizza/AMu$E`0R.CAZeXXXX/

```
Source   = 0x4d = M
Source = 0x5a = Z
Source   = 0x45 = E
Source   = 0x75 = u
Source   = 0x24 = $
Source   = Source = 0x41 = A
```

```

1.转到PE头(偏移量0x3c)
2.转到导出表(偏移量0x88)
3.转到名称表(偏移量0x20)
4.获取函数名称
5.比较名称
6.转到序数表(偏移量0x24)
7.获取功能号码
8.转到地址表(偏移量0x1c)
9.获取功能地址

MOV QWORD PTR ,RBX
MOV QWORD PTR ,RDI
MOV QWORD PTR ,RDX ;'beep'
MOV R8,RCX
TEST RCX,RCX
JE 000000000000007A
MOV EAX,00005A4D   ;'ZM'   PE头
CMP WORD PTR ,AX
JNE 000000000000007A
MOVSXD RAX,DWORD PTR
CMP DWORD PTR ,00004550;'PE' magic
JNE 000000000000007A
MOV R9D,DWORD PTR
ADD R9,RCX
MOV R11D,DWORD PTR
ADD R11,RCX
XOR ECX,ECX
CMP DWORD PTR ,ECX
JBE 000000000000007A
MOV EAX,DWORD PTR
MOV RBX,QWORD PTR
ADD RAX,R8
SUB RBX,RAX
MOV EDI,ECX
MOVZX EDX,BYTE PTR
MOVZX R10D,BYTE PTR
SUB EDX,R10D
JNE 000000000000006E
INC RAX
TEST R10D,R10D
JNE 0000000000000059
TEST EDX,EDX
JE 0000000000000087
INC ECX
CMP ECX,DWORD PTR
JB 0000000000000048
XOR EAX,EAX
MOV RBX,QWORD PTR
MOV RDI,QWORD PTR
RET
MOV ECX,DWORD PTR
ADD RCX,R8
MOVZX EDX,WORD PTR
MOV ECX,DWORD PTR
ADD RCX,R8
MOV EAX,DWORD PTR
ADD RAX,R8
JMP 000000000000007C
```


又完成一个套娃,继续下一个sub_140002910。又多了一段,在memcmp下断点,看看比较的啥





输入全是'X',输出全是'E',得与RUECKWAERTSINGENIEURWESEN相等,应该存在某种对应关系,将A~F作为输入看看,修改参数为:09CxxR@brUc3Exxx/1337pr.ost/20testtest/=pizza/AMu$E`0R.CAZeXXXX/ABCDEFGHIJKLMNOPQRSTUVWXYZ/



所以ABCDEFGHIJKLMNOPQRSTUVWXYZ对应着VPWLCJSFTXKHINGUBYZDOMQERA,用py推一下,修改参数为:09CxxR@brUc3Exxx/1337pr.ost/20testtest/=pizza/AMu$E`0R.CAZeXXXX/YPXEKCZXYIGMNOXNMXPYCXGXN/

```
table = str.maketrans("VPWLCJSFTXKHINGUBYZDOMQERA", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
key = "RUECKWAERTSINGENIEURWESEN"
output = key.translate(table)
print(output) # YPXEKCZXYIGMNOXNMXPYCXGXN
```

继续程序运行到sub_140002670,又多了一段字符串。。修改参数为:09CxxR@brUc3Exxx/1337pr.ost/20testtest/=pizza/AMu$E`0R.CAZeXXXX/YPXEKCZXYIGMNOXNMXPYCXGXN/XXXXXXXXXXX/



在0x26e1 有三个比较,动态调试,看看比较了什么。





0x6335626f转为字符就是'ob5c',改一下参数,继续调试下一个比较, 0x7255 对应 'Ur',其实它比较的就是第0段未使用的5个字符'brUc3'。

改参,调试下一个,只有一个33对应字符'3' , 修改参数:09CxxR@brUc3Exxx/1337pr.ost/20testtest/=pizza/AMu$E`0R.CAZeXXXX/YPXEKCZXYIGMNOXNMXPYCXGXN/ob5cUr3/



如果之前'0x1f+day' 即字符'=' 那个地方没注意,偏移0x27ff会判断失败程序退出,当然一个字符我们也可以爆破。如果第0段的'brUc3'不知道的话,就推不出ob5cUr3,那么byte_140049420就会解密错误的指令,影响后面指令的执行。运行到sub_140002670函数的最后,偏移0x290A,进入指令,看看干了啥,看到了call    qword ptr ,还有参数8,明显又套了一层娃,修改参数:09CxxR@brUc3E/1337pr.ost/20testtest/=pizza/AMu$E`0R.CAZe/YPXEKCZXYIGMNOXNMXPYCXGXN/ob5cUr3/XXXXXX/ ,将之前多余的X都去掉(影响程序运行)






调试一下就可以得到最后的答案了,最终参数为09CxxR@brUc3E/1337pr.ost/20testtest/=pizza/AMu$E`0R.CAZe/YPXEKCZXYIGMNOXNMXPYCXGXN/ob5cUr3/fin/




总结

该程序通过命令行参数,控制文件的生成,并对文件进行修改,还会校验文件内容,并且通过参数控制shellcode的执行。
这才第三题,对于我这种小白,不敢想象后面是啥。。。

参考链接

(https://github.com/omr00t/flareon10_solutions/blob/main/3_mypassion/README.md)
[官方solutions](https://www.mandiant.com/resources/blog/flareon10-challenge-solutions)

csl丶龙 发表于 2024-5-2 21:44

牛逼得很

Cleaf007 发表于 2024-5-4 08:42

硬核干货,学习一下

zhaijing521 发表于 2024-5-9 11:06

感谢楼主分享,收获很大!

Satifer 发表于 2024-5-10 07:57

感谢分享,收获颇丰

15022387787 发表于 2024-5-14 13:42

感谢分享,研究看看

wisoft 发表于 2024-5-21 23:14

这得做多久哦,太厉害了

wdb364778978 发表于 2024-9-25 09:04

硬通干货,谢谢分享

benaceone 发表于 2024-11-22 17:29

感谢分享,研究看看

jackflank 发表于 2024-11-23 17:21

太详细了大神
页: [1]
查看完整版本: flare-on ctf 2023 第三题mypassion分析