title: GKCTF X DASCTF应急挑战杯-Maple_root-Writeup
tags:

  • CTF
  • DASCTF
  • WP
    abbrlink: 748fffcf
    date: 2021-06-27 12:52:49

GKCTF X DASCTF应急挑战杯-Maple_root-Writeup

参赛队员:

b4tteRy, x0r, f1oat

最终成绩:2285

最终排名:27

总结

经过最近几次类线下的演练,感觉慢慢对CTF有点上手了,这次终于不再爆0了,继续努力

MISC

签到

wireshark打开可知是shell流量,命令结果编码为hex+base64。观察前面几条whoami/ls等命令输出可知每行输出都是倒序输出。
GKCTF-X-DASCTF应急挑战杯-Maple-root-Writeup - 图1
故将cat /f14g|base64的结果每行倒序拼接解码后得到flag的编辑记录,去除双写得到flag。
GKCTF-X-DASCTF应急挑战杯-Maple-root-Writeup - 图2

你知道apng吗

将apng转换为gif后查看,发现部分关键帧内有二维码。
GKCTF-X-DASCTF应急挑战杯-Maple-root-Writeup - 图3
GKCTF-X-DASCTF应急挑战杯-Maple-root-Writeup - 图4
GKCTF-X-DASCTF应急挑战杯-Maple-root-Writeup - 图5
GKCTF-X-DASCTF应急挑战杯-Maple-root-Writeup - 图6
GKCTF-X-DASCTF应急挑战杯-Maple-root-Writeup - 图7

扫描结果顺序拼接得到flag。

银杏岛の奇妙冒险

GKCTF-X-DASCTF应急挑战杯-Maple-root-Writeup - 图8
GKCTF-X-DASCTF应急挑战杯-Maple-root-Writeup - 图9
GKCTF-X-DASCTF应急挑战杯-Maple-root-Writeup - 图10
GKCTF-X-DASCTF应急挑战杯-Maple-root-Writeup - 图11

FireFox Forensics

下载下来解压以后有一个json文件和一个sql存储的db数据库文件,查看db数据库内容是加密的,又根据题目提示本题内容属于登录信息相关,应该是FireFox浏览器所存储的登录信息,在Github上已有相关的解密脚本,直接下载以后将题目给出的两个文件放入相同目录,使用python3 firepwd.py即可成功解密得到flag

excel骚操作

打开看到一行字,根据提示flag隐写在表格中。
依次查看单元格/解压查看数据可发现部分单元格值为1,查看单元格格式可发现格式为;;;,改变格式后正常显示出数字。
设置条件格式,使得所有值为1的单元格黑色填充,并拉伸所有数字范围内单元格至正方形,得到黑色单元格画成的汉信码。
GKCTF-X-DASCTF应急挑战杯-Maple-root-Writeup - 图12
使用中国编码app扫描得到flag。

Reverse

QQQQT

下载后使用PEID扫描是WIN32程序,拖入IDA32分析可看出程序显然是由Qt语言所编写,题目名称也印证了这一点,一开始在IDA的WinMain窗口中多次依照逻辑查找无果,之后又在OD中尝试进行动态调试,在分析出的字符串当中找到了flag相关的关键词已经其上下的文本地址GKCTF-X-DASCTF应急挑战杯-Maple-root-Writeup - 图13

GKCTF-X-DASCTF应急挑战杯-Maple-root-Writeup - 图14
然后在IDA中根据以上地址找到这些字符串所在的位置,可以得到如下函数

  1. int __thiscall sub_4012F0(_DWORD *this)
  2. {
  3. int v1; // edi
  4. _BYTE *v2; // esi
  5. const char *v3; // edx
  6. _BYTE *v4; // esi
  7. int v5; // ecx
  8. int v6; // eax
  9. int v7; // ecx
  10. int v8; // edx
  11. int v9; // edi
  12. int v10; // esi
  13. _BYTE *v11; // ecx
  14. unsigned int v12; // ecx
  15. int v14; // [esp-8h] [ebp-A8h]
  16. char v16[4]; // [esp+10h] [ebp-90h] BYREF
  17. char v17[4]; // [esp+14h] [ebp-8Ch] BYREF
  18. _BYTE *v18; // [esp+18h] [ebp-88h]
  19. const char *v19; // [esp+1Ch] [ebp-84h]
  20. int v20; // [esp+20h] [ebp-80h]
  21. int v21; // [esp+24h] [ebp-7Ch] BYREF
  22. _BYTE *v22; // [esp+28h] [ebp-78h] BYREF
  23. char v23[60]; // [esp+2Ch] [ebp-74h] BYREF
  24. __int128 v24[2]; // [esp+68h] [ebp-38h] BYREF
  25. __int64 v25; // [esp+88h] [ebp-18h]
  26. int v26; // [esp+9Ch] [ebp-4h]
  27. MEMORY[0x5FF6](*(_DWORD *)(this[6] + 4), v16);
  28. v26 = 0;
  29. MEMORY[0x7C7C](v16, v17);
  30. LOBYTE(v26) = 1;
  31. v19 = (const char *)MEMORY[0x7C48](v17);
  32. v24[0] = 0i64;
  33. v24[1] = 0i64;
  34. v25 = 0i64;
  35. strcpy(v23, "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz");
  36. v21 = 138 * strlen(v19) / 0x64;
  37. v14 = v21 + 1;
  38. v1 = 0;
  39. v22 = (_BYTE *)MEMORY[0x8114](v21 + 1);
  40. v2 = v22;
  41. sub_402C08(v22, 0, v14);
  42. v3 = v19;
  43. v20 = (int)(v19 + 1);
  44. if ( strlen(v19) )
  45. {
  46. v4 = &v2[v21];
  47. v18 = v4;
  48. while ( 1 )
  49. {
  50. v20 = ((char)*v4 << 8) + v3[v1];
  51. v5 = v20 / 58;
  52. *v4 = v20 % 58;
  53. if ( v5 )
  54. {
  55. do
  56. {
  57. v6 = (char)*--v4;
  58. v7 = (v6 << 8) + v5;
  59. v20 = v7 / 58;
  60. *v4 = v7 % 58;
  61. v5 = v20;
  62. }
  63. while ( v20 );
  64. v4 = v18;
  65. }
  66. if ( ++v1 >= strlen(v19) )
  67. break;
  68. v3 = v19;
  69. }
  70. v2 = v22;
  71. }
  72. v8 = 0;
  73. if ( !*v2 )
  74. {
  75. do
  76. ++v8;
  77. while ( !v2[v8] );
  78. }
  79. v9 = v21;
  80. if ( v8 <= v21 )
  81. {
  82. v10 = v2 - (_BYTE *)v24;
  83. do
  84. {
  85. v11 = (char *)v24 + v8++;
  86. *v11 = v23[(char)v11[v10]];
  87. }
  88. while ( v8 <= v9 );
  89. }
  90. if ( !MEMORY[0x7C1A](v24, "56fkoP8KhwCf3v7CEz") )
  91. {
  92. if ( v19 )
  93. v12 = strlen(v19);
  94. else
  95. v12 = -1;
  96. v22 = (_BYTE *)MEMORY[0x7CCC](v19, v12);
  97. LOBYTE(v26) = 2;
  98. v21 = MEMORY[0x7CCC]("flag", 4);
  99. LOBYTE(v26) = 3;
  100. MEMORY[0x6124](this, &v21, &v22, 1024, 0);
  101. MEMORY[0x7C66](&v21);
  102. MEMORY[0x7C66](&v22);
  103. }
  104. MEMORY[0x7C30](v17);
  105. return MEMORY[0x7C66]();
  106. }

由上方字符串123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz推测为Base58编码,将字符串56fkoP8KhwCf3v7CEz以Base58解码后尝试提交即为flag

Crash

打开发现本程序是用Golang编写的,因此使用IDA7.6打开即可对应大部分的字符集,打开main_main看第18行为

  1. if ( v1[1] == 43LL && *(_DWORD *)v0 == 1413696327 && *(_WORD *)(v0 + 4) == 31558 && *(_BYTE *)(v0 + 42) == 125 )

其中,各个等值式分别判定了TCKG(内存地址中倒序存储字符串),{F,},因此推断这里应该就是在进行flag的判定,接着进入main_check函数

  1. void __golang main_check(__int64 a1, unsigned __int64 a2)
  2. {
  3. __int64 v2; // [rsp+10h] [rbp-68h]
  4. __int64 v3; // [rsp+10h] [rbp-68h]
  5. __int64 v4; // [rsp+10h] [rbp-68h]
  6. __int64 v5; // [rsp+18h] [rbp-60h]
  7. __int64 v6; // [rsp+18h] [rbp-60h]
  8. __int64 v7; // [rsp+18h] [rbp-60h]
  9. __int64 v8; // [rsp+18h] [rbp-60h]
  10. __int64 v9; // [rsp+18h] [rbp-60h]
  11. __int64 v10; // [rsp+18h] [rbp-60h]
  12. __int64 v11; // [rsp+20h] [rbp-58h]
  13. __int64 v12; // [rsp+20h] [rbp-58h]
  14. __int64 v13; // [rsp+20h] [rbp-58h]
  15. __int64 v14; // [rsp+20h] [rbp-58h]
  16. __int64 v15; // [rsp+20h] [rbp-58h]
  17. __int64 v16; // [rsp+20h] [rbp-58h]
  18. __int64 v17; // [rsp+28h] [rbp-50h]
  19. __int64 v18; // [rsp+28h] [rbp-50h]
  20. char v19[32]; // [rsp+30h] [rbp-48h] BYREF
  21. char v20[32]; // [rsp+50h] [rbp-28h] BYREF
  22. if ( a2 < 0x1E )
  23. runtime_panicSliceAlen();
  24. v2 = main_encrypto(a1 + 6, 24LL);
  25. if ( v5 == 44 )
  26. {
  27. v11 = runtime_memequal(v2, (__int64)"o/aWPjNNxMPZDnJlNp0zK5+NLPC4Tv6kqdJqjkL0XkA=", 44LL, 44);
  28. if ( (_BYTE)v5 )
  29. {
  30. if ( a2 < 0x22 )
  31. runtime_panicSliceAlen();
  32. v17 = runtime_stringtoslicebyte((__int64)v19, a1 + 30, 4LL, v5, v11);
  33. Encrypt_HashHex2(v6, v12, v17, v6, v12);
  34. if ( v13 == 64 )
  35. {
  36. v14 = runtime_memequal(
  37. v7,
  38. (__int64)"6e2b55c78937d63490b4b26ab3ac3cb54df4c5ca7d60012c13d2d1234a732b74",
  39. 64LL,
  40. v7);
  41. if ( (_BYTE)v7 )
  42. {
  43. if ( a2 < 0x26 )
  44. runtime_panicSliceAlen();
  45. v18 = runtime_stringtoslicebyte((__int64)v20, a1 + 34, 4LL, v7, v14);
  46. Encrypt_HashHex5(v8, v15, v18, v8, v15);
  47. if ( v16 == 128 )
  48. {
  49. runtime_memequal(
  50. v9,
  51. (__int64)"6500fe72abcab63d87f213d2218b0ee086a1828188439ca485a1a40968fd272865d5ca4d5ef5a651270a52ff952d955c9"
  52. "b757caae1ecce804582ae78f87fa3c9",
  53. 128LL,
  54. v9);
  55. if ( (_BYTE)v9 )
  56. {
  57. if ( a2 < 0x2A )
  58. runtime_panicSliceAlen();
  59. main_hash(a1 + 38, 4LL, v3, v9);
  60. if ( v10 == 32 )
  61. runtime_memequal(v4, (__int64)"ff6e2fd78aca4736037258f0ede4ecf0", 32LL, 32);
  62. }
  63. }
  64. }
  65. }
  66. }
  67. }
  68. }

可以看出,总共分为了若干段,第一段有六个字符,且使用了DES加密(可直接爆破)第二段是使用了sha256加密,第三段是使用了sha512加密,最后一段则使用了md5加密,以上若干加密段皆可根据前面所截取提交内容内的长度推断该段加密前明文的长度,因此除第一个DES爆破解密已有相关网站外,其余皆写相同脚本爆破即可
第一段DES加密可以从encoding_json_Unmarshal内找到key和iv

  1. "key": "WelcomeToTheGKCTF2021XXX",
  2. "iv": "1Ssecret"

接着发现此处key共有24byte,因此推测为3des,因此进行相应的爆破即可得到第一段flag,后面几段的解题脚本如下:
sha256 -> 4位

  1. import hashlib
  2. import string
  3. import itertools
  4. dateset = string.ascii_lowercase + string.digits
  5. res = "6e2b55c78937d63490b4b26ab3ac3cb54df4c5ca7d60012c13d2d1234a732b74"
  6. def generate_strings(length=4):
  7. chars = string.ascii_lowercase + string.digits
  8. for item in itertools.product(chars, repeat=length):
  9. tmp = "".join(item)
  10. aa = hashlib.sha256(tmp.encode('utf-8')).hexdigest()
  11. if aa.hexdigest() == res:
  12. print(tmp)
  13. exit(0)
  14. generate_strings()

sha512 -> 4位

  1. import hashlib
  2. import string
  3. import itertools
  4. dateset = string.ascii_lowercase + string.digits
  5. res = "6500fe72abcab63d87f213d2218b0ee086a1828188439ca485a1a40968fd272865d5ca4d5ef5a651270a52ff952d955c9b757caae1ecce804582ae78f87fa3c9"
  6. def generate_strings(length=4):
  7. chars = string.ascii_lowercase + string.digits
  8. for item in itertools.product(chars, repeat=length):
  9. tmp = "".join(item)
  10. aa = hashlib.sha512(tmp.encode('utf-8')).hexdigest()
  11. if aa.hexdigest() == res:
  12. print(tmp)
  13. exit(0)
  14. generate_strings()

md5 -> 4位

  1. import hashlib
  2. import string
  3. import itertools
  4. dateset = string.ascii_lowercase + string.digits
  5. flag = "ff6e2fd78aca4736037258f0ede4ecf0"
  6. def generate_strings(length=4):
  7. chars = string.ascii_lowercase + string.digits
  8. for item in itertools.product(chars, repeat=length):
  9. md5 = hashlib.md5()
  10. tmp = "".join(item)
  11. md5.update(tmp.encode('utf-8'))
  12. if md5.hexdigest() == flag:
  13. print(tmp)
  14. exit(0)

将以上爆破出来的值按照顺序连在一起即为flag

Web

easycms

题目提示五位弱密码,尝试admin/12345后登陆成功。
尝试更改主题相关模板代码提示需要创建/system/tmp/xxxx.txt(貌似是动态的四个字母)。
在素材库上传文件可观察到路径是文件名直接拼接来的,故可利用目录穿越创建上述要求创建的文件。再向模板内插入shell后读flag即可。