freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

DEF CON CTF Qualifier 2022 not-so-advanced writeup
山石网科 2022-06-21 10:26:50 109650
所属地 广东省

not-so-advanced writeup

1655778149_62b12b65ea83014abc9e9.png!small

最终解答队伍数60 / 总得分队伍数470

这道题目放出的时候,看见后缀为gba文件。

先使用file 命令查看

1655778157_62b12b6d5ce8a67797068.png!small

之前没有接触过对应文件格式的内容,使用https://fileinfo.com/extension/gba

查看后得知

GBA文件是任天堂Game Boy Advance(GBA)视频游戏(如神奇宝贝、塞尔达或最终幻想)的ROM镜像。它包含一个GBA游 戏的文件,保存的格式可以使用game Boy模拟器玩。

1655778164_62b12b74650f3ee1f8668.png!small

一个任天堂gameboy游戏 ,因此下载对应的VisualBoy Advance模拟器,这里我使用的是http://www.mgba.io/downloads.html使用模拟器运行,界面很简单,只是输出了Read the code,should be easy

使用键盘上的上下按键能够更改字母,发现都是a-z小写字母, 回车键为确认,会输出信息

1655778176_62b12b80d60bfb863d169.png!small

通过运行程序大致了解到程序会检查输入的值,与对应的值比对,来显示出不同的提示信息。

IDA 载入能够看到一些字符串

1655778187_62b12b8be364d523c8d5f.png!small

但代码段无法进行反编译,

1655778197_62b12b95dec997e0223f1.png!small

尝试使用Ghidra进行逆向,找到了关于GBA的插件,https://github.com/SiD3W4y/GhidraGBA

这里踩了一个坑,本机上使用的Ghidra版本是最新版,插件版本为10.1.导致Ghidra无法加载下载的插件,因此重新下载了10.1版本的Ghidra ,花了一些时间。 下载对应版本的Ghidra就可以直接载入插件了,就能对gba文件分析了。

插件放入的具体位置

1655778205_62b12b9d9dd89791e8b79.png!small

在Ghidra中新建项目 ,就能识别出来了

1655778217_62b12ba949788966c2312.png!small

直接看entry

1655778225_62b12bb105c778a8db698.png!small

能看到字符串

"abcdefghijklmnopqrstuvwxyz_"

这个可能会作为最后解题所需的内容。

再根据界面上显示出来的值 进行关键信息定位

1655778234_62b12bba6ca56a713e18c.png!small

交叉引用

很容易就找到了程序对应的源码以及伪代码

1655778241_62b12bc16e21e22b095f3.png!small

很明显 FUN_08000568 就是main函数

FUN_08000e74就是printf函数,main函数代码如下

longlong main(void)

{
int iVar1;
uint in_lr;
undefined auStack268 [256];
undefined uStack12;

FUN_080009cc();
printf("Read the code, should be easy\n");
uStack12 = 0;
FUN_08000340(auStack268);
printf("Checking it out\n");
iVar1 = FUN_08000298(auStack268);
if(iVar1 == 0x12e1) {
printf("That works");
}
else{
printf(&DAT_08005ce4);
}
return(ulonglong)in_lr << 0x20;
}

对应的汇编代码为

**************************************************************
*                                                            *
*  FUNCTION                                                  *
**************************************************************
undefined main()
assume LRset = 0x0
assume TMode = 0x1
undefined         r0:1           <RETURN>
main
08000568 80 b5           push       { r7, lr }
0800056a c2 b0           sub        sp,#0x108
0800056c 00 af           add        r7,sp,#0x0
0800056e 00 f0 2d fa     bl FUN_080009cc                                     undefined FUN_080009cc()
08000572 16 4b           ldr        r3=>s_Read_the_code,_should_be_easy_08005ca4,[   = "Read the code, should be easy
= 08005ca4
08000574 18 00           mov        r0=>s_Read_the_code,_should_be_easy_08005ca4,r3  = "Read the code, should be easy
08000576 00 f0 7d fc     bl printf                                           undefined printf()
0800057a 15 4b           ldr        r3,[DAT_080005d0]                                = FFFFFEFCh
0800057c 84 22           mov        r2,#0x84
0800057e 52 00           lsl        r2,r2,#0x1
08000580 9b 18           add        r3,r3,r2
08000582 da 19           add        r2,r3,r7
08000584 80 23           mov        r3,#0x80
08000586 5b 00           lsl        r3,r3,#0x1
08000588 00 21           mov        r1,#0x0
0800058a d1 54           strb       r1,[r2,r3]
0800058c 3b 1d           add        r3,r7,#0x4
0800058e 18 00           mov        r0,r3
08000590 ff f7 d6 fe     bl FUN_08000340                                     undefined FUN_08000340()
08000594 0f 4b           ldr        r3=>s_Checking_it_out_08005cc4,[PTR_s_Checking   = 08005cc4
= "Checking it out\n"
08000596 18 00           mov        r0=>s_Checking_it_out_08005cc4,r3                = "Checking it out\n"
08000598 00 f0 6cfc     bl printf                                           undefined printf()
0800059c 3b 1d           add        r3,r7,#0x4
0800059e 18 00           mov        r0,r3
080005a0 ff f7 7afe     bl FUN_08000298                                     undefined FUN_08000298()
080005a4 03 00           mov        r3,r0
080005a6 0c 4a           ldr        r2,[DAT_080005d8]                                = 000012E1h
080005a8 93 42           cmp        r3,r2
080005aa 04 d1           bne        LAB_080005b6
080005ac 0b 4b           ldr        r3=>s_That_works_08005cd8,[PTR_s_That_works_08   = 08005cd8
                                                = "That works"
080005ae 18 00           mov        r0=>s_That_works_08005cd8,r3                     = "That works"
080005b0 00 f0 60 fc     bl printf                                           undefined printf()
080005b4 03 e0           b          LAB_080005be
LAB_080005b6                                    XREF[1]:     080005aa(j) 
080005b6 0a 4b           ldr        r3=>DAT_08005ce4,[PTR_DAT_080005e0]              = 08005ce4
= 4Eh    N
080005b8 18 00           mov        r0=>DAT_08005ce4,r3                              = 4Eh N
080005ba 00 f0 5bfc     bl printf                                           undefined printf()
LAB_080005be                                    XREF[1]:     080005b4(j) 
080005be 00 23           mov        r3,#0x0
080005c0 18 00           mov        r0,r3
080005c2 bd 46           mov        sp,r7
080005c4 42 b0           add        sp,#0x108
080005c6 80 bc           pop        { r7 }
080005c8 02 bc           pop        { r1 }
080005ca 08 47           bx         r1
-- Flow Override: RETURN (TERMINATOR)

通过分析以上代码分析,main函数功能很少 ,只是获取输入并检查,

iVar1 = FUN_08000298(auStack268);

如果通过函数FUN_08000298的值处理后的值iVar1的值等于0x12E1,那么就可以通过程序,输出That works。

而不是我们随便输入的值 ,输出DAT_08005ce4中的值(Nope)

1655778256_62b12bd0b4ffcc4879567.png!small

输入点只能小写字母,确认后进行验证,看代码还要满足值等于 0x12e1 ,否则会输入nope

1655778264_62b12bd81f6695301429d.jpeg!small

现在就要着重分析函数FUN_08000298,其伪代码为

undefined8 FUN_08000298(int param_1)

{
int iVar1;
ushort extraout_r1;
ushort extraout_r1_00;
uint uVar2;
undefined4 in_lr;
uint local_18;
ushort local_14;
ushort local_12;

iVar1 = FUN_08001148(param_1);
local_12 = 1;
local_14 = 0;
if(iVar1 == 9) {
for(local_18 = 0; local_18 < 9; local_18 = local_18 + 1) {
thunk_FUN_08000cc8((uint)local_12 + (uint)*(byte *)(param_1 + local_18),0xfff1);
thunk_FUN_08000cc8((uint)local_14 + (uint)extraout_r1,0xfff1);
local_14 = extraout_r1_00;
local_12 = extraout_r1;
}
uVar2 = (uint)(local_14 ^ local_12);
}
else{
uVar2 = 0xffffffff;
}
returnCONCAT44(in_lr,uVar2);
}

从以上代码可以从循环中分辨出输入长度必须为9,

进入函数thunk_FUN_08000cc8,

1655778278_62b12be667b45a2da53db.png!small

其调用了fun_08000ba0,继续看fun_08000ba0伪代码

uint FUN_08000ba0(uint param_1,uint param_2)

{
uint uVar1;
uint uVar2;
int iVar3;
uint uVar4;
uint uVar5;
uint uVar6;
bool in_NG;
bool bVar7;

uVar6 = param_1 ^ param_2;
uVar4 = param_2;
if(in_NG) {
uVar4 = -param_2;
}
if(uVar4 - 1 == 0) {
if((int)param_2 < 0) {
param_1 = -param_1;
}
returnparam_1;
}
uVar5 = param_1;
if((int)param_1 < 0) {
uVar5 = -param_1;
}
if(uVar5 <= uVar4) {
if(uVar5 < uVar4) {
param_1 = 0;
}
if(uVar5 == uVar4) {
param_1 = (int)uVar6 >> 0x1f | 1;
}
returnparam_1;
}
if((uVar4 & uVar4 - 1) == 0) {
if(uVar4 < 0x10000) {
iVar3 = 0;
}
else{
uVar4 = uVar4 >> 0x10;
iVar3 = 0x10;
}
if(0xff < uVar4) {
uVar4 = uVar4 >> 8;
iVar3 = iVar3 + 8;
}
if(0xf < uVar4) {
uVar4 = uVar4 >> 4;
iVar3 = iVar3 + 4;
}
if(uVar4 < 5) {
uVar4 = iVar3 + (uVar4 >> 1);
}
else{
uVar4 = iVar3 + 3;
}
uVar5 = uVar5 >> (uVar4 & 0xff);
if((int)uVar6 < 0) {
uVar5 = -uVar5;
}
returnuVar5;
}
if((uVar4 & 0xe0000000) == 0) {
uVar4 = uVar4 << 3;
uVar2 = 8;
}
else{
uVar2 = 1;
}
for(; uVar4 < 0x10000000 && uVar4 < uVar5; uVar4 = uVar4 << 4) {
uVar2 = uVar2 << 4;
}
for(; uVar4 < 0x80000000 && uVar4 < uVar5; uVar4 = uVar4 << 1) {
uVar2 = uVar2 << 1;
}
uVar1 = 0;
while( true ) {
if(uVar4 <= uVar5) {
uVar5 = uVar5 - uVar4;
uVar1 = uVar1 | uVar2;
}
if(uVar4 >> 1 <= uVar5) {
uVar5 = uVar5 - (uVar4 >> 1);
uVar1 = uVar1 | uVar2 >> 1;
}
if(uVar4 >> 2 <= uVar5) {
uVar5 = uVar5 - (uVar4 >> 2);
uVar1 = uVar1 | uVar2 >> 2;
}
if(uVar4 >> 3 <= uVar5) {
uVar5 = uVar5 - (uVar4 >> 3);
uVar1 = uVar1 | uVar2 >> 3;
}
bVar7 = uVar5 == 0;
if(!bVar7) {
uVar2 = uVar2 >> 4;
bVar7 = uVar2 == 0;
}
if(bVar7) break;
uVar4 = uVar4 >> 4;
}
if((int)uVar6 < 0) {
uVar1 = -uVar1;
}
returnuVar1;
}

很复杂,进行了大量的变量的处理,其实关注这两行代码就可以了

1655778291_62b12bf380d15adf3e456.png!small

thunk_FUN_08000cc8((uint)local_12 + (uint)*(byte *)(param_1 + local_18),0xfff1);
thunk_FUN_08000cc8((uint)local_14 + (uint)extraout_r1,0xfff1);

param_1这里是输入的值

通过分析以上代码,输入长度为9个字符。

那么最开始的 Abcdefghijklmnopqrstuvwxyz_ 要作为字符串输入。

且最终返回值为0x12E1 因此暴力破解写算法

1655778299_62b12bfb03a0f42722e57.png!small

1655778306_62b12c021dc4dee3a60e5.png!small

1655778314_62b12c0a9b8ef252640ea.png!small

成功。所以最后的flag为 aaaaaanzb

# DefCon
本文为 山石网科 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
山石网科 LV.7
山石网科安研院
  • 94 文章数
  • 90 关注者
Linux内核攻击面研究
2023-03-13
详解Flask SSTI 利用与绕过技巧V2
2023-03-06
Shadowsocks 重定向攻击
2023-02-09