From 6814897ce2de08180b6537fcccfb104827e2a131 Mon Sep 17 00:00:00 2001 From: n0p <0x90@n0p.cc> Date: Thu, 25 Oct 2018 23:14:29 +0200 Subject: Init. --- README.md | 31 ++++++ entry.c | 233 +++++++++++++++++++++++++++++++++++++++++++++ forgetful_commander.c | 118 +++++++++++++++++++++++ packer.py | 54 +++++++++++ public/.gitkeep | 0 public/forgetful_commander | Bin 0 -> 25656 bytes 6 files changed, 436 insertions(+) create mode 100644 README.md create mode 100644 entry.c create mode 100644 forgetful_commander.c create mode 100644 packer.py create mode 100644 public/.gitkeep create mode 100755 public/forgetful_commander diff --git a/README.md b/README.md new file mode 100644 index 0000000..4365b85 --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +Forgetful Commander +=================== + +# Author +n0p + +# Description +All typos in the flag are on purpose (brute-force prevention...)! + + +# Challenge Text +``` +And you lost a key again. This time it's the key to your missiles command +station. However, the command station got a silent password override. Even +though your memory isn't that good, you don't remember the password either, +your technical skills are!. You've already dumped the binary, which checks the +password. Now you just have to reverse it! +``` + +# Setup +packer.py builds everything. Requires clang. + +# Solution +Simple packer. Main program checks the argument against some XOR stuff. +Depending on the trap flag, the argument is checked against the fail string. + +# Point Value +dynamic + +# Flag +flag{Just_type__Please__and_the_missles_will_be_launched.} diff --git a/entry.c b/entry.c new file mode 100644 index 0000000..f5bdf3d --- /dev/null +++ b/entry.c @@ -0,0 +1,233 @@ +/* + * clang -s -m32 -nostdlib -nodefaultlibs -fPIC -Wl,-shared entry.c -oentry + */ + +__attribute__((naked)) void entry() { + __asm__ volatile( + ".intel_syntax noprefix\n" + "push eax\n" + "pushad\n" + "pushfd\n" + + "sub esp, 0x5000\n" + + // get the pathname of the binary with readlink + "mov eax, 85\n" + // /proc/self/exe + "push 0x6578\n" + "push 0x652f666c\n" + "push 0x65732f63\n" + "push 0x6f72702f\n" + // pathname of the symlink + "mov ebx, esp\n" + // buf + "lea ecx, [esp + 0x4010]\n" + // bufsiz + "mov edx, 0xfff\n" + "int 0x80\n" + + // cleanup arguments from stack + "add esp, 0x10\n" + + "cmp eax, 0\n" + "jle fail\n" + + // save the length of the pathname + "mov edi, eax\n" + + // write newline at the end of the pathname + "lea ecx, [esp + 0x4000 + eax]\n" + "mov byte ptr [ecx], 0xa\n" + + // inc saved length, to also compare the newline + "inc edi\n" + + // open /proc/self/maps to get the base address of the binary + "mov eax, 5\n" + "push 0x737061\n" + "push 0x6d2f666c\n" + "push 0x65732f63\n" + "push 0x6f72702f\n" + "mov ebx, esp\n" + "xor ecx, ecx\n" + "int 0x80\n" + + // cleanup arguments from stack + "add esp, 0x10\n" + + "cmp eax, 0\n" + "jl fail\n" + + // save the fd + "mov esi, eax\n" + + // loop to get the program base + // read from /proc/self/maps + "read:" + "mov eax, 3\n" + "mov ebx, esi\n" + "mov ecx, esp\n" + "mov edx, 0x4000\n" + "int 0x80\n" + + "cmp eax, 0\n" + "jle close\n" + + // set maps iterator to 0 + "xor ecx, ecx\n" + // set pathname iterator to 0 + "xor edx, edx\n" + // ebp will be set to 0, if we got a match + "mov ebp, 1\n" + + "find_pathname:\n" + "cmp ecx, eax\n" + "jge find_pathname_finished\n" + + // char from the maps buffer + "movzx ebx, byte ptr [esp + ecx]\n" + // compare with char in the pathname buffer + // always inc maps iterator + "inc ecx\n" + "cmp byte ptr [esp + edx + 0x4000], bl\n" + "jnz find_pathname_no_match\n" + // inc pathname iterator + "inc edx\n" + // have we found the full pathname? + "cmp edx, edi\n" + "jnz find_pathname\n" + // jepp + "xor ebp, ebp\n" + "jmp find_pathname_finished\n" + + "find_pathname_no_match:\n" + // reset pathname iterator + "xor edx, edx\n" + "jmp find_pathname\n" + + "find_pathname_finished:\n" + + // did we have a match? + "test ebp, ebp\n" + "jnz read\n" + // jepp + // search the beginning of the maps line + "find_beginning_of_line:" + "xor ebx, ebx\n" + "dec ecx\n" + // is the maps iterator non null? + "cmp ecx, 0\n" + "setg bl\n" + // is the char before the maps iterator not a '\n' + "cmp byte ptr [esp + ecx - 1], 0xa\n" + "setnz bh\n" + "test bl, bh\n" + "jnz find_beginning_of_line\n" + + // convert the 8 hex digits to 4 bytes => the base address + "xor edx, edx\n" + "mov ebp, ecx\n" + "add ecx, 8\n" + "convert_base:" + "cmp ebp, ecx\n" + "jge close\n" + "rol edx, 4\n" + + // check if it's a number + "xor ebx, ebx\n" + "cmp byte ptr [esp + ecx + ebp], 0x30\n" + "setge bl\n" + "cmp byte ptr [esp + ecx + ebp], 0x39\n" + "setle bh\n" + "sub byte ptr [esp + ecx + ebp], 0x30\n" + "xor dl, byte ptr [esp + ecx + ebp]\n" + "inc ebp\n" + "test bl, bh\n" + "jnz convert_base\n" + // wasn't a number, revert stuff + "dec ebp\n" + "xor dl, byte ptr [esp + ecx + ebp]\n" + "add byte ptr [esp + ecx + ebp], 0x30\n" + // check if it's a lowercase hex letter + "xor ebx, ebx\n" + "cmp byte ptr [esp + ecx + ebp], 0x61\n" + "setge bl\n" + "cmp byte ptr [esp + ecx + ebp], 0x7a\n" + "setle bh\n" + "sub byte ptr [esp + ecx + ebp], 0x57\n" + "xor dl, byte ptr [esp + ecx + ebp]\n" + "inc ebp\n" + "test bl, bh\n" + "jnz convert_base\n" + // wasn't a lowercase hex letter, revert stuff + "dec ebp\n" + "xor dl, byte ptr [esp + ecx + ebp]\n" + "add byte ptr [esp + ecx + ebp], 0x61\n" + // check if it's an uppercase hex letter + "xor ebx, ebx\n" + "cmp byte ptr [esp + ecx + ebp], 0x41\n" + "setge bl\n" + "cmp byte ptr [esp + ecx + ebp], 0x5a\n" + "setle bh\n" + "sub byte ptr [esp + ecx + ebp], 0x37\n" + "xor dl, byte ptr [esp + ecx + ebp]\n" + "inc ebp\n" + "test bl, bh\n" + "jnz convert_base\n" + // wasn't an uppercase hex letter, failed to convert + "xor edx, edx\n" + "jmp close\n" + + "jmp read\n" + + "close:\n" + // close the fd + "mov eax, 6\n" + "mov ebx, esi\n" + "int 0x80\n" + + // if the base hasn't been found quit + "test edx, edx\n" + "jz fail\n" + + // set the oep as return + "add edx, 0x2050\n" + "mov dword ptr [esp + 0x5024], edx\n" + "sub edx, 0x50\n" + + "mov ebp, eax\n" + "inc eax\n" + "inc eax\n" + + "dec_outer_loop:\n" + "mov esi, 0x466c7578\n" + "xor ebx, ebx\n" + "cmp eax, ebp\n" + "jz dec_outer_loop_finished\n" + + "mov ecx, 0x400\n" + + "dec_inner_loop:\n" + "xor dword ptr [edx], ebx\n" + "xor dword ptr [edx], esi\n" + "ror esi, 8\n" + "mov ebx, dword ptr [edx]\n" + "add edx, 4\n" + "loop dec_inner_loop\n" + + "dec eax\n" + "jmp dec_outer_loop\n" + + "dec_outer_loop_finished:\n" + + "add esp, 0x5000\n" + "popfd\n" + "popad\n" + "ret\n" + + "fail:\n" + // exit the program gracefully + "mov eax, 1\n" + "xor ebx, ebx\n" + "int 0x80"); +} diff --git a/forgetful_commander.c b/forgetful_commander.c new file mode 100644 index 0000000..e538503 --- /dev/null +++ b/forgetful_commander.c @@ -0,0 +1,118 @@ +/* + * clang -s -m32 -pie forgetful_commander.c -oforgetful_commander_unpacked + */ + +#include + +const char data[] = "\x4f\xb0\xab\x36\x1e\xb9\x59\x88\xa1\xe1\xf4\xef\x2f\x97" + "\x77\x83\x34\xa8\xe1\x70\x6f\x2c\xbe\x06\xc6\xb7\xd2\xa3" + "\x24\x1e\xf1\x78\xed\xdc\x4f\x9e\xa0\xb2\xf6\x10\xdf\xbe" + "\x33\xb4\x88\xf8\xeb\xe2\xc0\x1c\xed\x07\x0e\xe5\xb4\xde" + "\x07\xee\x6a\x5c\xb3\xe8\x37\x71\x8b\xf5\x6e\xf3\x33\xf0" + "\x86\x50\xf5\x15\x8b\xed\x84\x77\x1e\x7b\x02\xe0\x56\x5e" + "\x93\xba\x1a\x8c\x0f\xd2\xeb\x16\xb3\x83\x98\xfc\xd2\x81" + "\x87\xf3\xa0\x27\x5a\x4f\xe2\x38\x6f\xa0\x6c\xdd\x1d\x11" + "\x65\x69\xde\xe7\x27\x89\xe2\x95\xb6\x48\x39\x00\xf1\x8b" + "\x60\x1f\xfd\x8b\x73\x8f\x7d\x68\xd7\x3a\x19\x6d\x3a\x02" + "\x8a\xc5\x90\x25\x8c\x27\x77\x74\x8c\xeb\x90\x1f\x7a\x25" + "\xf8\x6a\x61\x8c\x4c\xa6\x56\x0c\xfb\x4a\x65\xb4\xeb\x12" + "\x9d\x27\xb7\x42\x8d\x9d\xec\x76\x96\x8e\xca\x86\x0f\xb2" + "\xc4\x14\x9f\x05\xba\xa7\x43\x7a\x7f\x2b\xf9\x33\x36\x1e" + "\xcf\x55\x63\x8a\xe2\x3b\x68\x02\x3f\x18\xd9\xbb\x6d\x8d" + "\xe5\x4b\xbe\x8f\x6c\x58\x1d\xfe\x17\x62\xb8\xa6\x8f\xb0" + "\xf7\x2b\x14\xc0\xb6\xf0\x2c\x25\x02\x2f\x2b\x79\xd8\x41" + "\x66\x52\x7b\xc6\x88\x72\x47\x31\x0c\x37\x6e\x34\xe2\x2c" + "\xd4\x95\x43\x68\x09\x26\xbb\x93\x24\x3f\x5a\x66\x41\xc4" + "\xdc\xaf\xf4\xa2\xa0\x00\x55\x23\x1a\x09\x3c\x51\xa0\xfa" + "\xa6\xda\x4c\x29\x5a\x6d\x24\x94\x98\x60\xcb\x19\x4e\xe7" + "\x32\x7c\x98\x4c\x6e\x0a\x21\xcd\x8e\xa8\x73\x73\x15\x0b" + "\x55\xad\xb9\x22\x53\x23\x33\x3f\x28\xb5\x50\x64\x56\xc8" + "\x5d\x4e\x89\x2a\x5f\xe5\x94\xe6\x7b\xc4\x15\x1b\x42\x70" + "\x4b\x19\x0f\xec\x0d\x4f\x9a\x1f\x6d\x3f\x10\x1e\x03\x98" + "\x8b\x1b\x56\x22\xfd\x77\x0a\x01\x5e\xa6\x36\xd9\xde\x22" + "\x03\xed\xba\xab\xb5\xcf\x1c\x72\x04\x41\x92\x34\xa4\xeb" + "\xde\xa9\x41\x1d\x20\xb0\xd6\x52\x19\xec\xb4\x08\x10\xc7" + "\x3f\xa2\x1e\x1a\x61\x38\x0b\x8b\x02\x46\x1c\x4e\x53\x4e" + "\xa0\x73\xb7\x7c\x2c\x2e\x0c\xd9\xfd\xd6\x3b\x34\xbb\xf7" + "\xea\xe3\xc9\x1b\x26\x8a\x33\x8f\xaf\xd7\x7a\xad\x72\x19" + "\xf1\x92\x79\xdf\xa9\x9d\x9c\x19\xc6\x82\x9a\xfd\x64\x7a" + "\xfe\xdb\x56\xc4\x54\x88\xb9\x26\x4c\xd7\xb6\xd6\xc2\x4b" + "\x71\x91\x8e\xfa\xb1\xdd\xfc\x9b\x29\xdc\xa8\x4e\xe7\x75" + "\x31\xf0\x99\x72\x3c\x4f\x7a\x42\x9d\x9d\x60\x9e\xf9\xfa" + "\xd6\xbb\x71\xfd\x19\x4b\x5d\x8d\x7c\x9f\x62\xd3\x14\x11" + "\xee\x5d\x7a\x5d\x97\xae\x96\xfb\x79\xc5\x7c\xf6\xc0\x55" + "\xbd\x7a\xc7\x81\x48\x64\xe7\x3b\xcf\x27\xd6\xcc\xd2\x39" + "\xce\x30\xce\x9d\xf9\xfa\x4a\x9e\x0b\x28\xf3\x39\x6b\xcf" + "\xa1\x3a\x4a\xf5\x70\x0e\x54\x66\x8b\x7d\x56\x68\x6e\x7e" + "\xc5\xde\x6f\x9c\xd0\xc8\x83\x90\x4d\xb1\xbc\x90\xef\x85" + "\xd8\xc3\x76\x3c\x72\x9f\x74\x75\x72\xd5\xbd\x33\xd7\xb5" + "\x8d\xe7\x59\xa2\x7a\x20\x81\xce\x47\x85\x56\x15\xc2\xb3" + "\x5b\x30\x6c\xf7\x45\xf1\x0e\xb3\x11\x8f\x20\x76\x7a\xd8" + "\xdd\x47\x8f\xe2\x43\x8c\xf0\x97\x33\x91\xce\xb1\xf5\x3f" + "\x8d\x42\x06\xa6\xd7\x77\xc3\xc3\xe2\xd2\x20\x50\x71\xbc" + "\x9c\x6f\xac\xad\x2f\x99\xb0\xf4\xb3\xf7\x73\x68\xbc\xf2" + "\x35\x33\x37\x9c\x56\x46\x81\xf9\xd2\xf2\x25\x1b\xdb\xe6" + "\x85\xfc\x30\x2d\xd1\x94\x6a\x2d\x13\x1a\xfa\xc5\x95\xcc" + "\x0c\xd2\xc1\xc9\x88\xf9\x66\x65\x18\x50\x52\x3a\x3b\x89" + "\xda\xed\xc0\x92\x22\x60\x75\x21\xa5\x7e\x8d\xe0\x84\x5e" + "\xb6\x0d\xc3\xbe\xfd\x31\xcf\x8b\x40\xf6\x97\xa1\x80\xaa" + "\xb7\x46\x38\xc7\x08\x81\xc3\x79\xbc\x25\xb3\xc8\x94\x66" + "\x54\x0d\xd0\x3b\x83\x8c\xd8\x28\x4e\x75\xcf\x6c\x29\xce" + "\x25\xd2\xbb\xaa\xab\xd7\x89\x49\x7d\xaf\xad\x8e\x1a\x73" + "\xe5\x61\x2e\x44\x3d\x6f\x38\xd9\x98\x76\xa5\x16\xc8\xbb" + "\x91\x46\x18\x0c\x86\x6a\x62\x7d\xfb\xea\x2f\xad\x3d\x4d" + "\x4d\x30\x37\x3a\x97\x13\x49\x36\xfe\x15\xfd\x2b\xdc\x3d" + "\x08\x8c\xb1\x25\x4a\xda\x0f\xbd\x41\xcf\xdc\xe7\xaf\x9a" + "\x4c\x59\x68\xcd\x03\x38\x6c\xf1\x2d\xb2\x1b\x95\x1b\x75" + "\x75\xae\x0e\x0e\xcf\xed\x52\x3e\x2d\x53\x79\xbb\x17\xa5" + "\x74\xb6\x9b\x0e\xa6\x75\xe4\x8b\xea\x6e\x91\xe6\xf3\x03" + "\xa5\x56\xe4\x4a\x18\x5d\x75\x5d\x7f\x7b\x4e\xea\xd2\x8f" + "\x39\x1d\x68\xe6\x52\x64\xce\x14\x9d\x35\x84\x64\xdc\x4e" + "\xb0\x10\x59\x1d\xc1\x3d\x60\xe7\xd3\x3e\xfe\x21\xcc\x95" + "\xfa\xf9\x1e\x74\xf8\x3c\x44\x2f\xef\x1f\x80\xac\xea\xdc" + "\x30\xfd\x21\x05\xa7\x35\xf1\x97\xfa\x4a\x39\x78\x38\x5f" + "\xef\xdc\x48\xfb\x76\xfc\x42\x46\xce\xfc\x29\x3f\xf2\xe6" + "\x51\x01\xb2\xb7\x32\x9c\x73\xc4\x7e\xa7\x14\x06\x97\xe9" + "\x00\x01\x87\x57\x6f\x3b\x88\x09\xac\xbd\x21\xbe\x57\x9a" + "\xcf\x10\xfd\x81\x2c\x10\xbd\x46\x75\xae\x41\xaf\xa6\xe4" + "\x78\x84\xc0\xaa\x74\x4b"; + +const char xor[] = "\xdf\x98\xe2\x08\xcc\xbb\xeb\xac\x8c\xb2\xaa\xca\x85\xe3" + "\xb2\x5d\xea\x87\x99\xc1\x4b\x78\xb8\xe9\xea\x1d\x5e\xd5" + "\x53\xf8\x0f\x09\xd9\xde\x05\x7c\x69\x1a\x6d\xbd\x6f\x8c" + "\x34\xd4\x74\x4e\x1c\x24\x5d\x83\x1d\x4a\xa7\xc8\x6c\xc2" + "\x43\xb6"; + +int main(int argc, char *argv[]) { + if (argc != 2) { + return 1; + } + + int matchCounter = 0, offset = 5, key = 5; + + for (int i = 0; i < strlen(argv[1]); i++) { + char tmp = argv[1][i]; + + __asm__ volatile( + "pushfd\n" + "pop %%edx\n" + "mov %%edx, %%ecx\n" + "and $0x100, %%ecx\n" + "xor %%ecx, %%edx\n" + "ror $2, %%ecx\n" + "xor %%ecx, %%edx\n" + "push %%edx\n" + "mov %1, %%edx\n" + "popfd\n" + "cmovz %%ecx, %%edx\n" + "mov %%edx, %0\n" + : "=r"(offset) + : "r"(offset)); + + if (tmp == (xor[i] ^ data[offset + i * key])) matchCounter++; + } + + if (matchCounter == 58) return 0; + + return 1; +} diff --git a/packer.py b/packer.py new file mode 100644 index 0000000..b74a5ac --- /dev/null +++ b/packer.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python2 + +import lief +import os +import subprocess + +subprocess.call(["clang", "-s", "-m32", "-nostdlib", "-nodefaultlibs", "-fPIC", "-Wl,-shared", "entry.c", "-oentry_tmp"]) +subprocess.call(["clang", "-s", "-m32", "-pie", "forgetful_commander.c", "-oforgetful_commander_tmp"]) + +entry = lief.parse("entry_tmp") +forgetful_commander = lief.parse("forgetful_commander_tmp") + +segment_added = forgetful_commander.add(entry.segments[1]) + +forgetful_commander.header.entrypoint = segment_added.physical_address + +# Encrypt exec segment +content = forgetful_commander.segments[3].content + +clr = [0, 0, 0, 0] +flux = [0x78, 0x75, 0x6c, 0x46] +for i in xrange(0, len(content), 4): + tmp = content[i:i+4] + content[i] ^= flux[0] ^ clr[0] + content[i+1] ^= flux[1] ^ clr[1] + content[i+2] ^= flux[2] ^ clr[2] + content[i+3] ^= flux[3] ^ clr[3] + flux.append(flux.pop(0)) + clr = tmp + +forgetful_commander.segments[3].content = content +forgetful_commander.segments[3].add(lief.ELF.SEGMENT_FLAGS.W) + +# Encrypt ro segment +content = forgetful_commander.segments[4].content + +clr = [0, 0, 0, 0] +flux = [0x78, 0x75, 0x6c, 0x46] +for i in xrange(0, len(content), 4): + tmp = content[i:i+4] + content[i] ^= flux[0] ^ clr[0] + content[i+1] ^= flux[1] ^ clr[1] + content[i+2] ^= flux[2] ^ clr[2] + content[i+3] ^= flux[3] ^ clr[3] + flux.append(flux.pop(0)) + clr = tmp + +forgetful_commander.segments[4].content = content +forgetful_commander.segments[4].add(lief.ELF.SEGMENT_FLAGS.W) + +forgetful_commander.write("public/forgetful_commander") + +os.remove("entry_tmp") +os.remove("forgetful_commander_tmp") diff --git a/public/.gitkeep b/public/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/public/forgetful_commander b/public/forgetful_commander new file mode 100755 index 0000000..339d27c Binary files /dev/null and b/public/forgetful_commander differ -- cgit v1.2.3