
초보자를 위한 펌웨어 해킹 접근법
이 문서는 펌웨어 리버싱과 익스플로잇에 입문한 초보자인 제 개인적인 경험을 담고 있습니다.

데모를 위해, 우리는 CVE-2023-27216을 분석하고 재현할 것입니다.
펌웨어를 익스플로잇하기 위해 다음 단계가 있습니다:
gdbserver를 정적으로 빌드합니다.일반적으로 펌웨어 바이너리 파일은 부트로더(uBoot), 커널 파일, 부트로더용 커널 헤더(uImage), 압축된 파일 시스템(일반적으로 SquashFS 형식), CRC/MD5 테이블(파일 무결성 확인용) 및 기타 여러 파일들을 포함합니다.
먼저 펌웨어를 분석할 방법을 찾고, 연구를 수행하여 몇 가지 리소스를 얻었습니다:

Extract firmware using binwalk: binwalk -Me DSL-3782_A1_EU_1.01_07282016.bin

추출된 squashfs-root 폴더와 몇 가지 이상한 파일들을 얻었습니다.

보너스: squashfs-root 폴더가 보이지 않는다면, 보이는 ".squashfs" 파일에 unsquashfs를 사용하세요. 그냥 zip 파일과 같습니다😅.
펌웨어 아키텍처와 엔디언을 확인합니다. 펌웨어에서 추출한 일부 바이너리를 확인하여 이를 확인할 수 있습니다. 아키텍처와 펌웨어 확인: file <binary>

여기서 펌웨어가 MIPS 32비트 MSB 아키텍처에서 실행된다는 것을 거의 확신할 수 있습니다. "거의"라고 한 이유는 일부 펌웨어가 Lexra와 같은 MIPS 호환 아키텍처에서 실행될 수 있기 때문입니다.
squashfs-root 폴더를 확인하고 몇 가지 흥미로운 파일을 발견했습니다:
usr/etc/init.d/rcS => 펌웨어가 부팅될 때 실행되는 스크립트입니다usr/etc/passwd => 사용자 정보가 포함된 파일입니다userfs/romfile.cfg => 자격 증명 admin:admin이 있습니다rcS 파일을 확인하고 흥미로운 코드를 발견했습니다:
echo "admin:$1$$iC.dUsGpxNNJGeOm1dFio/:0:0:root:/:/bin/sh" > /usr/etc/passwd
passwd 파일에 쓰는 데 사용됩니다Boa는 2000년대에 라우터와 같은 임베디드 장치에서 주로 사용된 고대 웹서버입니다. 그러나 boa 서버는 2005년에 이미 개발이 중단되었습니다! Boa 서버가 거의 20년 전에 죽었지만, 우리 공급업체 덕분에 오늘날까지 살아 있습니다.

에뮬레이션 프로세스에는 Ubuntu 또는 Kali와 같은 Debian 기반 OS를 권장합니다. 펌웨어 해킹에 특화된 또 다른 OS인 AttifyOS도 있습니다. 이 문서에서는 Kali Linux를 사용했습니다. 에뮬레이션 과정은 두 가지 도구로 시작합니다:

FAT를 사용하여 펌웨어를 완전히 에뮬레이트하는 방법을 살펴보겠습니다. 먼저 GitHub에서 Kali 머신으로 레포지토리를 클론합니다. 그런 다음 설정 과정을 진행합니다. fat.config 파일도 변경해야 합니다. 그렇지 않으면 작동하지 않습니다.
git clone https://github.com/attify/firmware-analysis-toolkit.git
cd firmware-analysis-toolkit
./setup.sh
vi fat.config # Modify to your sudo password.
그런 다음 펌웨어 바이너리(제조사에서 다운로드한 것)를 Kali 머신의 FAT 폴더로 복사하고 실행합니다.
./fat.py DSL-3782_A1_EU_1.01_07282016.bin
참고: FAT 설정 과정에서 오류가 발생할 수 있습니다. libmagic이 없다고 나올 수 있습니다.

간단히 다음을 실행하세요
pip unistall python-magic
pip install python-magic
이렇게 하면 문제가 해결되며, 빌드 명령을 다시 실행합니다. 이제 완벽하게 작동할 것입니다.

Enter를 눌러 실행합니다. 에뮬레이션 프로세스가 잘 작동하여 http://192.168.1.1 (Kali 머신에서)로 접속하여 작동 여부를 확인할 수 있습니다.

자격 증명이 있으면 콘솔에 로그인할 수도 있습니다. 자격 증명은 admin:admin입니다.

에뮬레이션된 펌웨어를 종료하려면 Ctrl+A X를 누르세요. 다시 실행해야 할 때는 fat.py를 다시 실행하지 마십시오. 펌웨어가 이미 이미지로 빌드되었기 때문입니다. 이미 생성된 스크립트만 실행하면 됩니다.
cd firmadyne/scratch/<Image-ID>
./run.sh

디버깅 목적으로 gdbserver를 빌드합니다. gdbserver를 빌드하는 방법은 여러 가지가 있습니다. 정적으로 빌드된 서버를 다운로드할 수도 있습니다. 정적으로 빌드된 일부 파일을 저장한 repo가 있습니다. 그러나 저는 gdbserver를 직접 빌드하는 것을 선호합니다. GitHub repo에 있는 것은 꽤 오래되어 호환성 문제가 발생할 수 있기 때문입니다.
참고를 위해 이 블로그 게시물을 참조하세요: https://sheran.sg/blog/cross-compile-gdb-for-mips/. 블로그는 2024년 7월 30일에 업로드되었으며, 이 프로젝트 직전이므로 완벽하게 작동합니다.
참고: 블로그는 MIPS x32 LSB용으로 빌드되었지만 우리는 MIPS x32 MSB가 필요합니다. mipsel-linux-gnu를 mips-linux-gnu로 변경해야 합니다.
MIPS용 툴체인을 설치해야 합니다. 다행히 Debian 패키지에 이미 있습니다.
**apt update && apt upgrade -y
apt install -y build-essential m4 gcc-mips-linux-gnu g++-mips-linux-gnu**
MIPS용 gdbserver를 빌드하려면 빌드 및 설치해야 할 몇 가지 패키지가 있습니다. 다음은 소스를 가져온 곳입니다.
소스 다운로드
wget https://sourceware.org/pub/gdb/releases/gdb-15.1.tar.xz
wget https://gmplib.org/download/gmp/gmp-6.3.0.tar.xz
wget https://www.mpfr.org/mpfr-current/mpfr-4.2.1.tar.xz
툴체인으로 라이브러리 빌드 이 라이브러리들을 빌드할 때 루트 권한이 있어야 합니다. MPFR을 빌드할 때 필요하기 때문에 먼저 GMP를 빌드해야 합니다.
tar xvf gmp-6.3.0.tar.xz && cd gmp-6.3.0
./configure --host=mips-linux-gnu
make -j$((`nproc`+1))
make install
cd ..
그런 다음 MPFR을 빌드합니다:
tar xvf mpfr-4.2.1.tar.xz && cd mpfr-4.2.1
./configure --host=mipsel-linux-gnu --with-gmp-build=<YOUR-FOLDER>/gmp-6.3.0
make -j$((`nproc`+1))
make install
cd ..
이제 마침내 gdbserver를 빌드할 수 있습니다:
tar xvf gdb-15.1.tar.xz && cd gdb-15.1
./configure --host=mipsel-linux-gnu --with-gmp-lib=/usr/local/lib --with-mpfr-lib=/usr/local/lib --with-gmp-include=<YOUR-FOLDER>/gmp-6.3.0 --with-mpfr-include=<YOUR-FOLDER>/mpfr-4.2.1/src
make -j$((`nproc`+1)) LDFLAGS=-static
빌드된 바이너리 gdbserver는 gdb-15.1/gdbserver 폴더에 있어야 합니다.
에뮬레이션된 펌웨어에는 wget, nc, curl, /dev/tcp 등이 없습니다. 파일 전송을 위해 Python HTTP 서버를 호스팅할 수 없습니다. ssh도 없습니다. 그러나 이미지를 마운트하여 에뮬레이션된 머신에 gdbserver를 넣을 수 있습니다.
sudo ./scripts/mount.sh 1gdbserver를 마운트된 폴더의 아무 곳에나 복사합니다.sudo ./scripts/umount.sh 1./run.sh를 다시 실행합니다).

이제 Kali 머신 내에서 디버깅과 해킹을 수행할 수 있습니다. 그러나 에뮬레이션된 머신을 호스트 머신(Windows 또는 Mac)으로 포트 포워딩하여 한 단계 더 나아갈 수 있습니다.
먼저 ifconfig를 사용하여 네트워크를 확인합니다.

결과는 두 개의 인터페이스가 있음을 알려줍니다: eth0과 tap1_0. 우리가 알고 있는 바로는, 호스트의 공유 네트워크 인터페이스인 eth0과 에뮬레이션된 펌웨어 머신의 인터페이스인 tap1_0입니다.
이해를 돕기 위해, eth0 네트워크는 호스트 머신에서 Kali 머신에 접근할 수 있는 공용 네트워크와 같습니다. tap1_0은 Kali 머신에서만 접근할 수 있는 사설 네트워크와 같습니다. eth0에서 tap1_0 인터페이스의 포트 192.168.1.1:80으로 연결을 전달해야 합니다.
이를 도와줄 수 있는 많은 도구가 있습니다. 그러나 iptables가 가장 잘 작동하는 것 같습니다. 물론 설정 방법을 안다면 말이죠.
먼저 포트 포워딩을 허용해야 합니다. 다음 명령을 실행합니다:
echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward
이것은 한 세션에만 적용됩니다. 영구적으로 적용하려면 /etc/sysctl.conf의 내용을 수정하십시오.
net.ipv4.ip_forward=1 # Find this line, uncomment it.
완료되면 파일을 저장하고 닫습니다.
그런 다음 이 파일의 설정을 적용합니다. 다음 명령을 실행합니다:
sudo sysctl -p
sudo sysctl --system
일반적으로 많은 iptables 명령을 실행할 수 있습니다. 하지만 너무 지루할 것입니다 😵💫. iptables-persistent 도구를 설치할 수 있습니다. 이를 통해 설정 파일을 작성하고, 파일로 로드하거나 체인을 파일로 추출할 수 있습니다. 모두 빠르게 수행할 수 있습니다.
apt install iptables-persistent
여기서 수정하려는 설정 파일은 /etc/iptables/rules.v4입니다. 파일의 내용을 아래 내용으로 변경합니다.
*filter
:INPUT ACCEPT [37:22880]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [35:2330]
# Forward HTTP Port
-A FORWARD -i eth0 -o tap1_0 -p tcp --dport 80 -d 192.168.1.1 -j ACCEPT
-A FORWARD -i tap1_0 -o eth0 -p tcp --sport 80 -s 192.168.1.1 -j ACCEPT
# Forward Debugger port
-A FORWARD -i eth0 -o tap1_0 -p tcp --dport 31337 -d 192.168.1.1 -j ACCEPT
-A FORWARD -i tap1_0 -o eth0 -p tcp --sport 31337 -s 192.168.1.1 -j ACCEPT
COMMIT
# Completed on Wed Aug 7 09:32:11 2024
# Generated by iptables-save v1.8.10 (nf_tables) on Wed Aug 7 09:32:11 2024
*nat
:PREROUTING ACCEPT [60:5405]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [1096:50947]
-A PREROUTING -i eth0 -p tcp -j DNAT --to-destination 192.168.1.1
-A POSTROUTING -o tap1_0 -p tcp -d 192.168.1.1 -j MASQUERADE
주의: 모든 포트를 허용하면 많은 보안 문제가 발생합니다. 모든 포트를
DROP한 다음 원하는 몇 개만FORWARD하는 것이 좋습니다.
저장하고 iptables 체인을 갱신합니다.
service netfilter-persistent reload
이제 호스트 외부에서 접근할 수 있습니다.

익스플로잇할 여러 엔드포인트가 있습니다. 그 중 2개는 cfg_manager 바이너리 내에 있습니다. 저는 그 중 1개만 시연하고, 나머지는 여러분이 직접 알아내도록 하겠습니다.
바이너리를 좋아하는 디컴파일러에 던지고, 모든 system 명령을 확인하면 이것을 볼 수 있습니다. 이 명령은 /etc/lanconfig.sh라는 파일을 실행합니다.

이 파일을 사용할 수 있는 다른 위치를 확인하던 중, 파일을 쓸 수 있는 곳을 발견했습니다.

설명하면 다음과 같습니다:
/etc/lanconfig.sh를 엽니다mxmlElementGetAttr 함수를 호출하는데, 이는 객체에서 속성을 찾는 것으로 추정되며, HTTP 요청에서 직접 또는 간접적으로 올 수 있고 XML일 수 있습니다.mxmlElementGetAttr에서 얻은 속성으로 sprintf를 사용하여 문자열을 만듭니다.fputs를 사용하여 파일에 씁니다.즉시 웹 폴더 boaroot에서 IP, netmask와 관련된 것을 검색했고 이것을 발견했습니다. boa 웹서버에 대한 문서는 매우 제한적이어서, POST 파라미터 lan_ip1을 바이너리에서 호출되는 XML의 IP 파라미터에 넣는 것이라고 추측할 수밖에 없습니다.

인터페이스에서 버그를 트리거하는 요청을 찾을 수 있습니다. 설정 > 네트워크에 있습니다.

저장을 누를 때 Burpsuite로 요청을 가로챕니다.

페이로드 192.168.1.1;utelnetd -p 8090 -l /bin/sh;는 리버스 셸입니다. 연결하여 실행할 수 있습니다.

비슷하며, 시도해보지는 않았지만 FAT보다 나을 수 있습니다 -> FirmAE.
Binary Ninja는 학생 신분이면 74$입니다. 라이선스는 누구나 공유할 수 있습니다.
CVE와 관련된 다른 버그들:

이것도 RCE로 이어질 수 있습니다. 여러분이 직접 하도록 남겨두겠습니다. 해당 위치 data_4c0160의 메모리는 어딘가에 주입될 수 있습니다🫡.

