
Zyxel 라우터 펌웨어와 MIPS 아키텍처를 파헤쳐 보며 재미를 느껴보세요.
이 튜토리얼은 정보 제공 및 교육 목적으로만 제공됩니다. 저는 윤리적 해킹, 정보 보안 및 사이버 보안이 디지털 정보와 컴퓨터를 사용하는 모든 사람에게 친숙한 주제여야 한다고 믿습니다. 저는 해커로부터 자신을 방어하려면 해킹이 어떻게 이루어지는지 알아야 한다고 생각합니다.
$ IDA Pro +7.0 (IDA가 비싸므로 Radar2를 사용할 수 있습니다)
$ Rompager 4.07을 실행하는 라우터: TP-Link TD-W8951*, ZyXEL P-660R, Billion BiPAC 51**/52** 등 엄청나게 많습니다.
$ UART to USB
Misfortune cookie 취약점은 꽤 오랫동안 알려져 왔지만, 공개적으로 취약점의 기술적 세부 사항을 설명하는 분석은 여전히 부족합니다. Check Point에 따르면 영향을 받는 소프트웨어는 AllegroSoft의 임베디드 웹 서버 RomPager입니다. 인터넷 전체 스캔 결과 RomPager는 사용 가능한 엔드포인트 수 측면에서 세계에서 가장 인기 있는 웹 서버 소프트웨어일 가능성이 높습니다. RomPager는 일반적으로 장치와 함께 출시되는 펌웨어에 내장됩니다. 이 특정 취약점은 2002년에 코드 베이스에 도입되었습니다. 또한 노출된 장치는 버전 4.34 이전(특히 4.07)의 RomPager 서비스를 사용하는 장치입니다.
이 튜토리얼에서는 Rompager 4.07을 사용하는 ZXHN H108L을 사용하겠습니다.
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm="ZXHN H108L"
Content-Type: text/html
Transfer-Encoding: chunked
Server: RomPager/4.07 UPnP/1.0
EXT:
이제 시리얼 포트를 사용해 보겠습니다. 먼저 Baud rate를 결정해야 합니다.
$ sudo stty -F /dev/ttyUSB0
출력 :
speed 115200 baud; line = 0;
-brkint -imaxbel
device
저는 __minicom__을 사용합니다. __screen__에서 __HyperTerminal__까지 무엇이든 사용할 수 있지만 안정성 때문에 이걸 선호합니다 ;)
$ sudo minicom -s
+-----------------------------------------------------------------------+
| A - Serial Device : /dev/modem |
| B - Lockfile Location : /var/lock |
| C - Callin Program : |
| D - Callout Program : |
| E - Bps/Par/Bits : 115200 8N1 |
| F - Hardware Flow Control : No |
| G - Software Flow Control : No |
| |
| Change which setting? |
+-----------------------------------------------------------------------+
| Screen and keyboard |
| Save setup as dfl |
| Save setup as.. |
| Exit |
| Exit from Minicom |
+--------------------------+
이제 준비가 되었습니다:
$ sudo minicom -D /dev/ttyUSB0 -b 115200
Bootbase Version: VTC_SPI1.26 | 2012/12/26 16:00:00
RAM: Size = 8192 Kbytes
Found SPI Flash 2MiB GD25Q16 at 0xbfc00000
SPI Flash Quad Enable
Turn off Quad Mode
RAS Version: ZXHN H108LV4.0.0a_ZRQ_MA
System ID: $2.12.162.0(G94.BY.2)3.20.19.0| 2014/03/21 20140321_v006 | 2014
Press any key to enter debug mode within 3 seconds.
..........
가능한 명령어를 표시하려면:
$ ATHE
======= Debug Command Listing =======
AT just answer OK
ATHE print help
ATBAx change baudrate. 1:38.4k, 2:19.2k, 3:9.6k 4:57.6k 5:115.2k
ATENx,(y) set BootExtension Debug Flag (y=password)
ATSE show the seed of password generator
ATTI(h,m,s) change system time to hour:min:sec or show current time
ATDA(y,m,d) change system date to year/month/day or show current date
ATDS dump RAS stack
ATDT dump Boot Module Common Area
ATDUx,y dump memory contents from address x for length y
ATRBx display the 8-bit value of address x
ATRWx display the 16-bit value of address x
ATRLx display the 32-bit value of address x
ATGO(x) run program at addr x or boot router
ATGR boot router
ATGT run Hardware Test Program
ATRTw,x,y(,z) RAM test level w, from address x to y (z iterations)
ATSH dump manufacturer related data in ROM
ATDOx,y download from address x for length y to PC via XMODEM
ATTD download router configuration to PC via XMODEM
ATUR upload router firmware to flash ROM
< press any key to continue >
이 펌웨어에는 동적 분석에 매우 유용한 숨겨진 명령어 세트가 있습니다. 문제는 레지스터를 볼 수 없고 중단점을 설정할 수 없다는 것입니다. 하지만 다행히도 이러한 숨겨진 명령어를 사용하면 해당 기능을 사용할 수 있습니다. 이를 보고 사용하려면 __ATEN__을 사용해야 합니다. 그러나 이 명령어는 비밀번호가 필요합니다. 그럼 어떻게 해야 할까요? 펌웨어를 리버스 엔지니어링하여 비밀번호 생성기의 알고리즘을 찾아봅시다.
(정적 분석) 먼저 펌웨어 파일을 살펴보겠습니다:
$ binwalk ras
binwalk1
$ dd if=ras of=rom1.lzma bs=1 skip=85043 count=66696
$ dd if=ras of=rom2.lzma bs=1 skip=350259 count=3350556
$ lzma -d <rom1.lzma> rom1
$ lzma -d <rom2.lzma> rom2
이제 추출된 파일에서 시그니처를 살펴보겠습니다:
$ binwalk -Y rom1
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 MIPS executable code, 32/64-bit, big endian, at least 1250 valid instructions
__string__은 바이너리 파일이 무엇인지 또는 무엇을 포함하는지 파악하는 훌륭한 도구입니다. 비밀번호 알고리즘을 찾을 수 있는지 확인해 봅시다:
$ strings rom1 | grep -i aten
$ strings rom2 | grep -i aten
분명히 펌웨어가 여전히 압축되어 있어서 볼 수 없습니다. 즉, 디버깅 모드를 사용하여 라우터에서 덤프해야 합니다:
$ ATDO bfc00000, 1f0000
xmodem
가장 먼저 떠오르는 것은 디스패치 테이블을 찾기 시작하는 것입니다. 이유는? 직관일 뿐입니다. 유일한 대안은 if-elif이지만 매우 긴 집합일 것입니다. 사용자가 제공한 비밀번호의 유효성을 확인하기 위한 것입니다. 다음은 제가 찾은 내용입니다:
ROM:800158A0 CmdTable: .word unk_80015C20 # DATA XREF: sub_8000E3D8+4Co
ROM:800158A0 # sub_8000E3D8+90o ...
ROM:800158A4 .word sub_8000E6D0
ROM:800158A8 .byte 0
ROM:800158A9 .byte 0
ROM:800158AA .byte 0
ROM:800158AB .byte 0
ROM:800158AC .word aJustAnswerOk # " just answer OK"
ROM:800158B0 .word aAthe # "ATHE"
ROM:800158B4 .word CMD_SHOW_HELP_ATHE
ROM:800158B8 .byte 0
ROM:800158B9 .byte 0
ROM:800158BA .byte 0
ROM:800158BB .byte 0
ROM:800158BC .word aPrintHelp # " print help"
ROM:800158C0 .word aAtba # "ATBA"
ROM:800158C4 .word CMD_ATBA
...
ROM:800158D0 .word aAten # "ATEN"
ROM:800158D4 .word CMD_ATEN
MIPS 단어는 4바이트라는 점을 기억하세요. 이제 __CMD_ATEN__을 살펴보겠습니다:
ROM:8000FF84 lw $a0, 8($s0)
ROM:8000FF88 jal sub_80013654
ROM:8000FF8C li $a1, 0
ROM:8000FF90 jal passwd_generator
그리고 이제: passwd_generator
ROM:80010A2C passwd_generator: # CODE XREF: CMD_ATEN+50p
ROM:80010A2C lui $t9, 0xA11F
ROM:80010A30 lw $v1, ptrDO_GTABLE
ROM:80010A34 lw $t8, dword_80017050
ROM:80010A38 lw $v1, 0x18($v1) # ATSE 명령어로 생성된 시드 부분을 가져옵니다.
ROM:80010A3C lbu $t8, 0x6D($t8) # MAC 주소의 마지막 바이트를 가져옵니다.
ROM:80010A40 sll $v1, 8 # $v1 = v1 << 8
ROM:80010A44 andi $t7, $t8, 7 # $t7 = t8 & 0x7
ROM:80010A48 li $t8, 0 # $t8 = 0
ROM:80010A4C srl $v1, 8 # $v1 = v1 >> 8
ROM:80010A50 li $t9, 0xA11F5AC6 # $t9 = MAGIC
ROM:80010A54 j loc_80010A68
ROM:80010A58 addu $t9, $v1, $t9 # $t9 = $v1 + $t9
ROM:80010A5C # ---------------------------------------------------------------------------
ROM:80010A5C
ROM:80010A5C loc_80010A5C: # CODE XREF: passwd_generator+40j
ROM:80010A5C sll $t9, 31 # $t9 = $t9 << 31
ROM:80010A60 or $t9, $t6, $t9 # $t9 = $t6 | $t9
ROM:80010A64 addiu $t8, 1 # $t8 = $t8 + 1
ROM:80010A68
ROM:80010A68 loc_80010A68: # CODE XREF: passwd_generator+28j
ROM:80010A68 sltu $t6, $t8, $t7 # $t6 = (t8 < t7? 1:0)
ROM:80010A6C bnez $t6, loc_80010A5C # $t6 != 0이면 점프
ROM:80010A70 srl $t6, $t9, 1 # $t6 = $t9 >> 1
ROM:80010A74 jr $ra # 모두 완료, 종료
ROM:80010A78 xor $v0, $t9, $v1 # $v0 = $t9 ^ $v1
결론을 내릴 수 있는 것:
이제 동일한 작업을 수행하는 C 프로그램을 만들고 컴파일해 보겠습니다:
U32 passwd_generator(U32 seed, U8 last_mac_octet)
{
U32 b = seed & 0x00FFFFFF;
U32 r = ROR(b + 0xA11F5AC6, last_mac_octet & 7);
return r^b;
}
이제 __ATSE__를 실행하여 시드를 얻고 사용합니다(예를 들어, 088521A78838을 얻었습니다):
$ ./gen_pass 088521A78838
방금 해킹한 마법 명령어를 실행해 보겠습니다 :p
$ ATEN1, A12F5AC6
ATEN1, A12F5AC6
OK
ATHE
======= Debug Command Listing =======
AT just answer OK
ATHE print help
ATBAx change baudrate. 1:38.4k, 2:19.2k, 3:9.6k 4:57.6k 5:115.2k
ATENx,(y) set BootExtension Debug Flag (y=password)
ATSE show the seed of password generator
ATTI(h,m,s) change system time to hour:min:sec or show current time
ATDA(y,m,d) change system date to year/month/day or show current date
ATDS dump RAS stack
ATDT dump Boot Module Common Area
ATDUx,y dump memory contents from address x for length y
ATWBx,y write address x with 8-bit value y
ATWWx,y write address x with 16-bit value y
ATWLx,y write address x with 32-bit value y
ATRBx display the 8-bit value of address x
ATRWx display the 16-bit value of address x
ATRLx display the 32-bit value of address x
ATGO(x) run program at addr x or boot router
ATGR boot router
ATGT run Hardware Test Program
AT%Tx Enable Hardware Test Program at boot up
ATBTx block0 write enable (1=enable, other=disable)
< press any key to continue >
ATRTw,x,y(,z) RAM test level w, from address x to y (z iterations)
ATWEa(,b,c,d) write MAC addr, Country code, EngDbgFlag, FeatureBit to flash ROM
ATCUx write Country code to flash ROM
ATCB copy from FLASH ROM to working buffer
ATCL clear working buffer
ATSB save working buffer to FLASH ROM
ATBU dump manufacturer related data in working buffer
ATSH dump manufacturer related data in ROM
ATWMx set low 6 digits MAC address in working buffer
ATMHx set hight 6 digits MAC address in working buffer
ATBS show the bootbase seed of password generator
ATLBx xmodem upload bootbase,x is password
ATSMx set 6 digits MAC address in working buffer
ATCOx set country code in working buffer
ATFLx set EngDebugFlag in working buffer
ATSTx set ROMRAS address in working buffer
ATSYx set system type in working buffer
ATVDx set vendor name in working buffer
ATPNx set product name in working buffer
ATFEx,y,... set feature bits in working buffer
ATMP check & dump memMapTab
ATDOx,y download from address x for length y to PC via XMODEM
< press any key to continue >
ATTD download router configuration to PC via XMODEM
ATUPx,y upload to RAM address x for length y from PC via XMODEM
ATUR upload router firmware to flash ROM
ATDC hardware version check disable during uploading firmware
ATLC upload router configuration file to flash ROM
ATUXx(,y) xmodem upload from flash block x to y
ATERx,y erase flash rom from block x to y
ATWFx,y,z copy data from addr x to flash addr y, length z
ATXSx xmodem select: x=0: CRC mode(default); x=1: checksum mode
ATLD Upload Configuration File and Default ROM File to Flash
ATBR Reset to default Romfile
ATCD Convert Running ROM File to Default ROM File into Flash
OK
마침내 메모리를 덤프할 수 있는 완전히 새로운 명령어 세트를 얻었습니다.
$ ATMP
ROMIO image start at bfc30000
code version:
code start: 80008000
code length: 1A5D4E
memMapTab: 18 entries, start = bfc44000, checksum = 9770
$RAM Section:
0: BootExt(RAMBOOT), start=80030000, len=18000
1: HTPCode(RAMCODE), start=80048000, len=E0000
2: RasCode(RAMCODE), start=80048000, len=420000
$ROM Section:
3: BootBas(ROMIMG), start=bfc28000, len=4000
4: DbgArea(ROMIMG), start=bfc2c000, len=2000
5: RomDir2(ROMDIR), start=bfc2e000, len=2000
6: BootExt(ROMIMG), start=bfc30030, len=13FD0
7: MemMapT(ROMMAP), start=bfc44000, len=C00
8: HTPCode(ROMBIN), start=bfc44c00, len=8000
(Compressed)
Version: HTP_TC V 0.05, start: bfc44c30
Length: 10488, Checksum: CB32
Compressed Length: 41CF, Checksum: D5A5
9: termcap(ROMIMG), start=bfc4cc00, len=400
10: RomDefa(ROMIMG), start=bfc4d000, len=2000
11: LedDefi(ROMIMG), start=bfc4f000, len=400
12: LogoImg(ROMIMG), start=bfc4f400, len=2000
13: LogoImg2(ROMIMG), start=bfc51400, len=2000
14: StrImag(ROMIMG), start=bfc53400, len=32000
15: StrImag2(ROMIMG), start=bfc85400, len=32000
16: Rt11nE2p(ROMIMG), start=bfcb7400, len=400
17: RasCode(ROMBIN), start=bfcb7800, len=246C00
(Compressed)
Version: ADSL ATU-R, start: bfcb7830
Length: 321D2C, Checksum: 582D
Compressed Length: 11E54B, Checksum: B30B
$USER Section:
Msecs 128
Heap0 16 300 16
Heap1 32 64 4
Heap2 64 64 4
Heap3 128 160 4
Heap4 192 256 4
Heap5 256 80 4
Heap6 320 20 4
Heap7 384 4 2
Heap8 448 20 2
Heap9 512 34 2
Heap10 1024 52 4
Heap11 2048 20 2
Heap12 3172 4 2
Heap13 4096 2 2
Heap14 0 0
...
방금 배운 내용은 이전에 추출한 롬 파일을 분석하는 데 도움이 될 것입니다.
code start: 80008000
code length: 1A5D4E
이제 다음 명령어를 사용하여 새 롬 파일을 덤프한 후 __ROM 시작 주소__와 __로딩 주소__를 __0x80008000__으로 설정할 수 있습니다.
$ ATDO 80008000, 1A5D4E
__ATGR__이 라우터를 부팅한다는 것을 알고 있습니다. 따라서 이를 추적하여 펌웨어의 2번째 부분으로 실행을 전달하는 명령어를 찾을 수 있습니다.
atgr1
ROM:80011E8C loc_80011E8C: # CODE XREF: LoadAndRunImage+D8j
ROM:80011E8C jal sub_80009B24
ROM:80011E90 li $a0, 0x1F4
ROM:80011E94
ROM:80011E94 execute_image: # execute image
ROM:80011E94 jalr $s0 # $s0에 있는 주소로 점프 및 연결
ROM:80011E98 nop
이제 $s0에 저장된 주소가 무엇인지 물어봐야 합니다. 분명히 중단점을 설정할 수 없고 레지스터를 볼 수 없으므로 몇 가지 트릭을 사용하여 이를 유출해야 합니다. 다행히도 메모리 읽기 및 쓰기가 가능합니다. 다음 명령어를 살펴보세요:
ATWL 80011E94, ae30001c
ATGR
보시다시피, 이 명령어는 __0x80011E94__의 명령어를 ae30001c(MIPS sw $s0, 0x1C($s1) 명령어의 바이트 표현)로 패치합니다. 원래 __jalr $s0__를 __sw $s0, 0x1C($s1)__로 덮어씀으로써 프로그램이 $s0 레지스터의 내용을 0x8001FF1C 메모리 주소에 저장하도록 강제했습니다. 따라서 두 번째 단계 펌웨어는 실행되지 않고 대신 "ERROR" 메시지가 표시되며 첫 번째 단계 콘솔에 계속 액세스할 수 있습니다. 만세! 이제 __0x8001FF1C__에 무엇이 저장되었는지 살펴보겠습니다.
ATRL 8001FF1C
8001FF1C: 80020000
잡았다! ... __0x80020000__을 사용하여 펌웨어의 2번째 부분을 덤프하고 적절하게 분석할 수 있습니다.
계속됩니다 ...