ctf夺旗赛
好久以前做过的ctf
web
第三个空白页
抓包,然后查看源码,
访问一下way=AHAHAH这个子目录
然后构造X-Forwarded-For: 127.0.0.1伪造ip成本地访问,
然后得到flag
控制中心的密码
一开始根据这个题目内容的描述我还以为是要进行爆破,但是又有验证码,所以我也是束手无策,后来摸索发现结果并不用爆破。
先登录和后台,在点击flag的位置抓包,
将1000改为1,上传,于是得到flag
又是空白页面
这个题目是最简单的,直接在检查里面就能看到flag
你就是长不了
这个题目也是简单抓包就可以,直接在包里面将长度改长就能跳出flag,
reverse
后门查杀
这个题目很简单,直接用d盾扫一下就能扫出后面,不想再下载附件了所以就讲思路得了
reverse1
这题能直接百度得到wp,
准备
获得信息
64位文件
x64dbg打开
搜索全部字符串
打开红框中字符串的位置
复制代码
1 00007FF7F26F18FD | 8945 04 | mov dword ptr ss:[rbp+4],eax | 2 00007FF7F26F1900 | 48:6345 04 | movsxd rax,dword ptr ss:[rbp+4] | 3 00007FF7F26F1904 | 48:8985 08010000 | mov qword ptr ss:[rbp+108],rax | 4 00007FF7F26F190B | 48:8D0D EEA60000 | lea rcx,qword ptr ds:[7FF7F26FC000] | 00007FF7F26FC000:"{hello_world}" 5 00007FF7F26F1912 | E8 8DF8FFFF | call reverse_1.7FF7F26F11A4 | 6 00007FF7F26F1917 | 48:8B8D 08010000 | mov rcx,qword ptr ss:[rbp+108] | 7 00007FF7F26F191E | 48:3BC8 | cmp rcx,rax | 8 00007FF7F26F1921 | 77 25 | ja reverse_1.7FF7F26F1948 | 9 00007FF7F26F1923 | 48:6345 04 | movsxd rax,dword ptr ss:[rbp+4] | 10 00007FF7F26F1927 | 48:8D0D D2A60000 | lea rcx,qword ptr ds:[7FF7F26FC000] | 00007FF7F26FC000:"{hello_world}" 11 00007FF7F26F192E | 0FBE0401 | movsx eax,byte ptr ds:[rcx+rax] | 12 00007FF7F26F1932 | 83F8 6F | cmp eax,6F | 6F:'o' 13 00007FF7F26F1935 | 75 0F | jne reverse_1.7FF7F26F1946 | 14 00007FF7F26F1937 | 48:6345 04 | movsxd rax,dword ptr ss:[rbp+4] | 15 00007FF7F26F193B | 48:8D0D BEA60000 | lea rcx,qword ptr ds:[7FF7F26FC000] | 00007FF7F26FC000:"{hello_world}" 16 00007FF7F26F1942 | C60401 30 | mov byte ptr ds:[rcx+rax],30 | 30:'0' 17 00007FF7F26F1946 | EB B0 | jmp reverse_1.7FF7F26F18F8 | 18 00007FF7F26F1948 | 48:8D0D E1830000 | lea rcx,qword ptr ds:[7FF7F26F9D30] | 00007FF7F26F9D30:"input the flag:" 19 00007FF7F26F194F | E8 7DF8FFFF | call reverse_1.7FF7F26F11D1 | 20 00007FF7F26F1954 | 48:8D55 28 | lea rdx,qword ptr ss:[rbp+28] | 21 00007FF7F26F1958 | 48:8D0D C5830000 | lea rcx,qword ptr ds:[7FF7F26F9D24] | 00007FF7F26F9D24:"%20s" 22 00007FF7F26F195F | E8 2BF9FFFF | call reverse_1.7FF7F26F128F | 23 00007FF7F26F1964 | 48:8D0D 95A60000 | lea rcx,qword ptr ds:[7FF7F26FC000] | 00007FF7F26FC000:"{hello_world}" 24 00007FF7F26F196B | E8 34F8FFFF | call reverse_1.7FF7F26F11A4 | 25 00007FF7F26F1970 | 4C:8BC0 | mov r8,rax | 26 00007FF7F26F1973 | 48:8D15 86A60000 | lea rdx,qword ptr ds:[7FF7F26FC000] | 00007FF7F26FC000:"{hello_world}" 27 00007FF7F26F197A | 48:8D4D 28 | lea rcx,qword ptr ss:[rbp+28] | 28 00007FF7F26F197E | FF15 4CE90000 | call qword ptr ds:[<&strncmp>] | 29 00007FF7F26F1984 | 85C0 | test eax,eax | 30 00007FF7F26F1986 | 74 0E | je reverse_1.7FF7F26F1996 | 31 00007FF7F26F1988 | 48:8D0D 81820000 | lea rcx,qword ptr ds:[7FF7F26F9C10] | 00007FF7F26F9C10:"wrong flag\n" 32 00007FF7F26F198F | E8 3DF8FFFF | call reverse_1.7FF7F26F11D1 | 33 00007FF7F26F1994 | EB 0C | jmp reverse_1.7FF7F26F19A2 | 34 00007FF7F26F1996 | 48:8D0D F3820000 | lea rcx,qword ptr ds:[7FF7F26F9C90] | 00007FF7F26F9C90:"this is the right flag!\n" 35 00007FF7F26F199D | E8 2FF8FFFF | call reverse_1.7FF7F26F11D1 |
代码分析
第28行代码调用了strcmp比较输入字符串与“{hello_world}”,这个“{hello_world}”似乎就是我们的flag。
在"input the flag"上面,分析代码,了解到此部分对“{hello_world}”进行了'o'字符替换为‘0’字符的操作。因此得到正确答案为“{hell0_w0rld}”
get flag!
flag{hell0_w0rld}
刮开有奖
本题直接能在百度上搜到,
进入主函数WinMain
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { DialogBoxParamA(hInstance, (LPCSTR)0x67, 0, DialogFunc, 0); return 0; }
找到关键的函数DialogFunc,并反编译为C代码
1 BOOL __userpurge DialogFunc@<eax>(int a1@<edi>, int a2@<esi>, HWND hDlg, UINT a4, WPARAM a5, LPARAM a6) 2 { 3 const char *v6; // esi 4 const char *v7; // edi 5 int v9; // [esp+4h] [ebp-20030h] 6 int v10; // [esp+8h] [ebp-2002Ch] 7 int v11; // [esp+Ch] [ebp-20028h] 8 int v12; // [esp+10h] [ebp-20024h] 9 int v13; // [esp+14h] [ebp-20020h] 10 int v14; // [esp+18h] [ebp-2001Ch] 11 int v15; // [esp+1Ch] [ebp-20018h] 12 int v16; // [esp+20h] [ebp-20014h] 13 int v17; // [esp+24h] [ebp-20010h] 14 int v18; // [esp+28h] [ebp-2000Ch] 15 int v19; // [esp+2Ch] [ebp-20008h] 16 CHAR String; // [esp+30h] [ebp-20004h] 17 char v21; // [esp+31h] [ebp-20003h] 18 char v22; // [esp+32h] [ebp-20002h] 19 char v23; // [esp+33h] [ebp-20001h] 20 char v24; // [esp+34h] [ebp-20000h] 21 char v25; // [esp+10030h] [ebp-10004h] 22 char v26; // [esp+10031h] [ebp-10003h] 23 char v27; // [esp+10032h] [ebp-10002h] 24 int v28; // [esp+20028h] [ebp-Ch] 25 int v29; // [esp+2002Ch] [ebp-8h] 26 27 __alloca_probe(); 28 if ( a4 == 272 ) 29 return 1; 30 v29 = a2; 31 v28 = a1; 32 if ( a4 != 273 ) 33 return 0; 34 if ( (_WORD)a5 == 1001 ) 35 { 36 memset(&String, 0, 0xFFFFu); 37 GetDlgItemTextA(hDlg, 1000, &String, 0xFFFF); 38 if ( strlen(&String) == 8 ) 39 { 40 v9 = 90; 41 v10 = 74; 42 v11 = 83; 43 v12 = 69; 44 v13 = 67; 45 v14 = 97; 46 v15 = 78; 47 v16 = 72; 48 v17 = 51; 49 v18 = 110; 50 v19 = 103; 51 sub_4010F0(&v9, 0, 10); 52 memset(&v25, 0, 0xFFFFu); 53 v6 = (const char *)sub_401000(&v25, strlen(&v25)); 54 memset(&v25, 0, 0xFFFFu); 55 v26 = v23; 56 v25 = v22; 57 v27 = v24; 58 v7 = (const char *)sub_401000(&v25, strlen(&v25)); 59 if ( String == v9 + 34 60 && v21 == v13 61 && 4 * v22 - 141 == 3 * v11 62 && v23 / 4 == 2 * (v16 / 9) 63 && !strcmp(v6, "ak1w") 64 && !strcmp(v7, "V1Ax") ) 65 { 66 MessageBoxA(hDlg, "U g3t 1T!", "@_@", 0); 67 } 68 } 69 return 0; 70 } 71 if ( (_WORD)a5 != 1 && (_WORD)a5 != 2 ) 72 return 0; 73 EndDialog(hDlg, (unsigned __int16)a5); 74 return 1; 75 }
-
代码分析
3.1 字符串解析
通过第37行代码GetDlgItemTextA,我们知道了String是我们输入的flag。
通过第38行代码我们知道flag的长度应该是8
第51行函数sub_4010F0在对v9~v19进行某种操作,进入sub_4010F0函数,将函数转换为C语言代码,再将v9~v19代入
#include <iostream> #include <stdlib.h> #include <stdio.h> using namespace std; int __cdecl sub_4010F0(char *a1, int a2, int a3) { int result; // eax int i; // esi int v5; // ecx int v6; // edx result = a3; for (i = a2; i <= a3; a2 = i) { v5 = i; v6 = a1[i]; if (a2 < result && i < result) { do { if (v6 >a1[result]) { if (i >= result) break; ++i; a1[v5] = a1[result]; if (i >= result) break; while (a1[i] <= v6) { if (++i >= result) goto LABEL_13; } if (i >= result) break; v5 = i; a1[result] = a1[i]; } --result; } while (i < result); } LABEL_13: a1[result] = v6; sub_4010F0(a1, a2, i - 1); result = a3; ++i; } return result; } char str[20] = { 90,74,83,69,67,97,78,72,51,110,103 }; int main() { cout << str << endl; sub_4010F0(str, 0, 10); for (int i = 0; i < 11; ++i) { cout << str[i]; } return 0; }
输出
3.2 字符串加密
分析第52行代码~58行代码。我们转到汇编代码处
.text:004012B0 push 0FFFFh ; size_t .text:004012B5 lea edx, [ebp+var_10004] .text:004012BB push 0 ; int .text:004012BD push edx ; void * .text:004012BE call _memset .text:004012C3 mov al, [ebp+var_1FFFF] .text:004012C9 mov dl, [ebp+var_1FFFD] .text:004012CF mov cl, [ebp+var_1FFFE] .text:004012D5 mov [ebp+var_10004], al .text:004012DB lea eax, [ebp+var_10004] .text:004012E1 mov [ebp+var_10002], dl .text:004012E7 add esp, 18h .text:004012EA mov [ebp+var_10003], cl .text:004012F0 lea edx, [eax+1] .text:004012F3 .text:004012F3 loc_4012F3: ; CODE XREF: DialogFunc+158↓j .text:004012F3 mov cl, [eax] .text:004012F5 inc eax .text:004012F6 test cl, cl .text:004012F8 jnz short loc_4012F3 .text:004012FA sub eax, edx .text:004012FC push eax .text:004012FD lea eax, [ebp+var_10004] .text:00401303 push eax .text:00401304 call sub_401000 .text:00401309 push 0FFFFh ; size_t .text:0040130E lea ecx, [ebp+var_10004] .text:00401314 push 0 ; int .text:00401316 push ecx ; void * .text:00401317 mov esi, eax .text:00401319 call _memset .text:0040131E mov al, [ebp+var_20001] .text:00401324 mov dl, [ebp+var_20002] .text:0040132A mov cl, [ebp+var_20000] .text:00401330 mov [ebp+var_10003], al .text:00401336 lea eax, [ebp+var_10004] .text:0040133C mov [ebp+var_10004], dl .text:00401342 add esp, 14h .text:00401345 mov [ebp+var_10002], cl .text:0040134B lea edx, [eax+1] .text:0040134E mov edi, edi
看加粗加红处(下面是对应字符串的信息)
-00020004 String db ? -00020003 var_20003 db ? -00020002 var_20002 db ? -00020001 var_20001 db ? -00020000 var_20000 db ? -0001FFFF var_1FFFF db ? -0001FFFE var_1FFFE db ? -0001FFFD var_1FFFD db ?
我们可以知道,v6使用sub_4010F0函数后的字符串的6,7,8位,调用sub_401000函数,v7使用sub_4010F0函数后的字符串的3,4,5位,调用sub_401000函数。
3.3 加密方式
进入sub_401000
1 _BYTE *__cdecl sub_401000(int a1, int a2) 2 { 3 int v2; // eax 4 int v3; // esi 5 size_t v4; // ebx 6 _BYTE *v5; // eax 7 _BYTE *v6; // edi 8 int v7; // eax 9 _BYTE *v8; // ebx 10 int v9; // edi 11 signed int v10; // edx 12 int v11; // edi 13 signed int v12; // eax 14 signed int v13; // esi 15 _BYTE *result; // eax 16 _BYTE *v15; // [esp+Ch] [ebp-10h] 17 _BYTE *v16; // [esp+10h] [ebp-Ch] 18 int v17; // [esp+14h] [ebp-8h] 19 int v18; // [esp+18h] [ebp-4h] 20 21 v2 = a2 / 3; 22 v3 = 0; 23 if ( a2 % 3 > 0 ) 24 ++v2; 25 v4 = 4 * v2 + 1; 26 v5 = malloc(v4); 27 v6 = v5; 28 v15 = v5; 29 if ( !v5 ) 30 exit(0); 31 memset(v5, 0, v4); 32 v7 = a2; 33 v8 = v6; 34 v16 = v6; 35 if ( a2 > 0 ) 36 { 37 while ( 1 ) 38 { 39 v9 = 0; 40 v10 = 0; 41 v18 = 0; 42 do 43 { 44 if ( v3 >= v7 ) 45 break; 46 ++v10; 47 v9 = *(unsigned __int8 *)(v3++ + a1) | (v9 << 8); 48 } 49 while ( v10 < 3 ); 50 v11 = v9 << 8 * (3 - v10); 51 v12 = 0; 52 v17 = v3; 53 v13 = 18; 54 do 55 { 56 if ( v10 >= v12 ) 57 { 58 *((_BYTE *)&v18 + v12) = (v11 >> v13) & 0x3F; 59 v8 = v16; 60 } 61 else 62 { 63 *((_BYTE *)&v18 + v12) = 64; 64 } 65 *v8++ = byte_407830[*((char *)&v18 + v12)]; 66 v13 -= 6; 67 ++v12; 68 v16 = v8; 69 } 70 while ( v13 > -6 ); 71 v3 = v17; 72 if ( v17 >= a2 ) 73 break; 74 v7 = a2; 75 } 76 v6 = v15; 77 } 78 result = v6; 79 *v8 = 0; 80 return result; 81 }
进入第65行byte_407830
.rdata:00407830 ; char byte_407830[] .rdata:00407830 byte_407830 db 41h ; DATA XREF: sub_401000+C0↑r .rdata:00407831 aBcdefghijklmno db 'BCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=',0
我们可以猜测这个函数应该是base64加密。
3.4 flag分析
if ( String == v9 + 34 // sub_4010F0函数后的第一位等于51+34=85-->'U' && v21 == v13 // 第2位,等于v13,即sub_4010F0函数返回值的第5位值-->'J' && 4 * v22 - 141 == 3 * v11 && v23 / 4 == 2 * (v16 / 9) && !strcmp(v6, "ak1w") // 第6,7,8行代码base64之后,需要等于"ak1w" && !strcmp( // 第3,4,5行代码,加密之后等于V1Ax v7, "V1Ax") ) { MessageBoxA(hDlg, "U g3t 1T!", "@_@", 0); }
将v6,v7解密之后得到WP1jMp,再结合第1,2位得到flag
3.get flag!
flag{UJWP1jMp}
icekey
不太会,不过flag也能在百度上查到,不过得费一番功夫,
GAesDecode
\1. APK**文件静态反编译**
使用JEB2反编译及查看JAVA代码(该题不涉及native层,IDA ARM反汇编及调试有机会再讲),“EP”定位可通过manifest查看
onCreate函数可看到很直接的加密流程:MainActivity.encrypt接受屏幕输入和一串字符;判断其返回值是否等于“kNk3Qz+l/kLpGuKxf5iGE9cOoTmmn9Ac+UdF4b2CHqU=”:
encrypt()函数传入的第二个参数“This is a AES-like encryption algorithm. However, we do some change. Therefore, you cannot directly use security class to decrypt the message. Our challenge is to find the plain text of this encrypt message with th fixed key.”
提示这是个变形的AES加密算法,可能是友善的也可能是误导,但后续我们可以此为依据进行验证加快解题速度。
\1. 尝试运行
使用雷电安卓模拟器,随意输入“12345678”,验证其错误信息输出
“Incorrect Flag!”:
\1. JEB2**调试**
Debugger->Start->Attach:
使用特定函数调用(如本例Base64.encodeToSTring)从JAVA定位到Smali代码,Ctrl+B下断点:
标准加密与APK调试验证对比:
\1. 破解算法
先参考下(AES加密算法的详细介绍与实现_TimeShatter的博客-CSDN博客_aes)标准的AES加密流程图:
APK算法首先对输入字串进行一个16分组,这里的变量名可通过快捷键n(类似IDA)重命名,帮助逆向理解:
对每16字符进行10轮变换
然后,做一个拼接(4是因为AES以4字节为一个单位):
最后对加密字串做base64输出。
对比关键的四个加密模块函数:字节代换substitute()、行移位shiftRows()、列混合mixCloumns()、轮密钥加addRoundKey()。
轮密钥加**addRoundKey():**
轮密钥加是将128位轮密钥Ki同状态矩阵中的数据进行逐位异或操作,与apk代码相符;
字节代换**substitute():**
可发现APK代码中的有现成的逆S盒与标准逆S盒一致,暂时可推断也使用标准S盒:
行移位**shiftRows():**
行移位是一个简单的左循环移位操作。当密钥长度为128比特时,状态矩阵的第0行左移0字节,第1行左移1字节,第2行左移2字节,第3行左移3字节,与APK代码一致:
列混合**mixCloumns():**
首先四次变换的逻辑没有问题:
2,3,1,1
1,2,3,1
1,1,2,3
3,1,1,2
LightState.add()也没有问题:
LightState.multiply2()发现异或值做了改变:
变形解密将“0x1b”改为“0x1d”配置好key及“kNk3Qz+l/kLpGuKxf5iGE9cOoTmmn9Ac+UdF4b2CHqU=”解密的16进制数
flag{aes_is_the_best_encryption}
杂项
hahaha
拿到题目发现是CRC32爆破,使用工具进行如下破解
得到压缩包密码为:
tanny_is_very_beautifu1_
解密后拿到flag.pdf,得到如下信息
需要我们进行排列组合,得到结果的Sha1为
e6079c5ce56e781a50f4bf853cdb5302e0d8f054
排列组合大致如下
1!2@{[}]asefcghnl
直接刚可能性太多,这里我们知道应该是flag{}样式,所以缩小范围为
1!2@sechn
编写如下脚本
import itertools import hashlib def sha1(str): sha = hashlib.sha1(str) encrypts = sha.hexdigest() return encrypts a1 = '1!' a2 = '2@' a3 = '{' a4 = '}' for str1 in itertools.combinations(a1,1): for str2 in itertools.combinations(a2,1): str3 = str1[0]+str2[0]+'sechn' for i in itertools.permutations(str3): tmp = (''.join(i)) res = 'flag{'+tmp+'}' # print sha1(res) if sha1(res) == 'e6079c5ce56e781a50f4bf853cdb5302e0d8f054': print res break
运行后得到flag
flag{sh@1enc}
再进行md5编码 947aed223b58dca829f38858116756c0
memory
这题随后求得密码是123456789
然后进行md5编码
Crypto
这里面的题目一般都在百度上直接能找到,这里就不过多阐述,
总结
此次比赛我主要是做web方向,完成情况也还不错,基本所以题上都是第一二个上交完成,其他题型的话基本上就是一个字摸,整体上来说完成情况还是不错的。
![Logo](https://devpress.csdnimg.cn/79de2bf0b7994defa4242ef90d5513fa.jpg)
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)