Sunday, November 19, 2017

HXP CTF 2017 - "revenge_of_the_zwiebel" Reversing 100 Writeup

revenge_of_the_zwiebel - 100 pts + 4 bonus pts ( 31 solves ):

After executing the binary it prints : "Input Key:" and waits for us to enter the flag. The routine printing the "Input Key:" message is executed at initialization alongside a sub-routine implementing the ptrace anti-debugging trick. Since we're going to debug the binary, I patched the anti-debugging sub-routine's address with nullsub_1.


After setting up remote debugging under IDA and supplying some random input to the binary we see a call to some code that was stored in executable memory.
 

IDA sometimes has trouble displaying memory under its debugger, so let's setup a manual memory region and set it as an executable 64-bit segment.

Now we should be able to view the entirety of the bytes copied to memory.
In the figure below, the code that is executed starts by checking if the 4th bit of input[0xC] is set. If it's not set, the message ":(" is printed and the process exits.


However, if the bit is set the code proceeds to decrypt the next block and also XOR the subsequent blocks that are still encrypted. (see figure below)


There's also a second test, implemented in some blocks, which involves the NOT instruction (figure below). This means that the 3rd bit of input[0x11] must not be set.

The amount of code executed is huge and doing this manually is impossible. So, we have two options : 

1.  Either dump the encrypted data, decrypt it statically and then build the flag by automatically reading the disassembly.
2.  Automate the IDA debugger to save the index plus the bits that must be set and guarantee that everything will be executed by continually patching ECX in order not to take the JECXZ jump.

Even if the 2nd attempt would take longer to complete, I chose to implement it. If I ever do the first one, I'll be sharing it here too :)

So, what the script below does is create a dictionary where the key is the character's position and the value is an array of bits that must be set. I simply ignore the NOT case, since we only care about the bits that must be set.

For example if the character at index 2 needs to have bits : 0, 4 and 7 set, the dictionary entry would look like this : {2: [1, 16, 80]}
After the process terminates, we proceed to OR the numbers of each array which gives us the flag in the end.

Here's the script that must be executed under IDA to get the flag.
Script runtime : approx. 15 minutes
Full script : here

# HXP CTF 2017 - revenge_of_the_zwiebel 100 pts
# Writeup link : https://rce4fun.blogspot.com/2017/11/hxp-ctf-2017-revengeofthezwiebel.html
# Souhail Hammou
from idc import *
from idaapi import *
def AddIfNotInDict(dict,index):
if index == -1:
raise Exception("Invalid index value !")
if index not in dict:
dict[index] = []
bin_dict = {}
RunTo(BeginEA())
GetDebuggerEvent(WFNE_SUSP,-1)
RunTo(0x4006A3) # CALL ECX
GetDebuggerEvent(WFNE_SUSP,-1)
StepInto()
GetDebuggerEvent(WFNE_SUSP,-1)
block_active = 0
is_not = 0
index = -1
try :
while True:
#read the current instruction
inst = GetDisasm(GetRegValue("RIP"))
if "mov cl, [rax+" in inst:
block_active = 1
try :
index = int(inst.split("+")[1].split("]")[0].split('h')[0],16)
except IndexError:
index = 0
AddIfNotInDict(bin_dict,index)
if block_active == 1:
if "not" in inst:
#NOT is executed before the AND instruction
#The bit is not set in the byte, so no need to save it
is_not = 1
elif "and" in inst and is_not == 0:
#we need to save the bit that must be set
bit = int(inst.split(",")[1].split(" ")[1].split("h")[0],16)
#The index was set previously at the MOV CL, [RAX+X] instruction
bin_dict[index].append(bit)
elif "jecxz" in inst :
#we reset our variables when we reach the JXCZ instruction
SetRegValue(1,"RCX") #do not branch
is_not = 0
block_active = 0
StepOver() #Next instruction
GetDebuggerEvent(WFNE_SUSP,-1)
except:
#process terminated
sweet_flag = ''
for index,bits in bin_dict.iteritems():
c = 0
for bit in bits:
c |= bit
sweet_flag += chr(c)
print "FLAG IS : " + sweet_flag
# FLAG IS : hxp{1_5m3ll_l4zyn355}
view raw ida_solve.py hosted with ❤ by GitHub
Follow me on Twitter : here

HXP CTF 2017 - "dont_panic" Reversing 100 Writeup

dont_panic - 100 pts + 15 bonus pts ( 19 solves ):
The file is a GO binary. After locating the main function, by stepping in the debugger, I found that the binary expects a command line argument of 42 characters.
For each character it calls a sub-routine sub_47B760 that returns a calculated byte corresponding to the one supplied. This byte is then compared to the one it corresponds to in a hardcoded array of bytes, which clearly represents the encrypted flag.

I didn't really look into how the values are calculated since GO binaries tend to be messy and I didn't really have to do it in order to solve the challenge. The thing is, the program branches to the block that displays the fail message ("Nope") as soon as it finds that one character is wrong. This opens room for brute-forcing since we can brute-force the flag character by character dynamically.

I used python with GDB to do so. Here's the solution script :
full script : here

#HXP CTF 2017 - dont_panic 100 pts
#Writeup link : https://rce4fun.blogspot.com/2017/11/hxp-ctf-2017-dontpanic-reversing-100.html
#Souhail Hammou
import gdb
CHAR_SUCCESS = 0x47B976
NOPE = 0x47BA23
gdb.execute("set pagination off")
gdb.execute("b*0x47B976") #Success for a given character
gdb.execute("b*0x47BA23") #Block displaying "Nope"
charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-+*{}'"
flag = list('A'*42) #junk
for i in range(0,len(flag)) :
for c in charset:
flag[i] = c
# the number of times we need to hit the
# success bp for the previous correct characters
success_hits = i
gdb.execute("r " + '"' + "".join(flag) + '"')
while success_hits > 0 :
gdb.execute('c')
success_hits -= 1
#we break either on success or on fail
rip = int(gdb.parse_and_eval("$rip"))
if rip == CHAR_SUCCESS:
break #right one. To the next character
if rip == NOPE: #added for clarity
continue
print("".join(flag))
#flag : hxp{k3eP_C4lM_AnD_D0n't_P4n1c__G0_i5_S4F3}
view raw go_solve.py hosted with ❤ by GitHub

After a minute or so, we get the flag.

HXP CTF 2017 - "Fibonacci" Reversing 100 Writeup

Fibonacci - 100 pts + 6 bonus pts ( 45 solves ):
This binary is supposed to print the flag directly into the screen. However, it will take a very very long time to print the whole flag since the output is based on the calculation of fibonacci numbers recursively.
For each bit of the encoded flag (length = 33 stored at 00000000004007E0), the fibonacci number of that bit's position is calculated : this means that it will calculate fibonacci values for numbers from 0 to 263.
This is not all. Since the flag needs to be decoded, each call to the fibonacci sub-routine expects a pointer to a bit value which is XORed with a calculated bit from the resulting fibonacci number. Keep in mind that the fibonacci implementation is recursive, and thus we expects this boolean value to be XORed multiple times for greater numbers.
When the fibonacci sub-routine returns to the main function, the corresponding bit of the encrypted flag is XORed with the calculated bit value.

The solution that came in mind is to modify the fibonacci implementation so as to save both the calculated bit value and the resulting fibonacci number for a given number. So instead of recursing and re-calculating the fibonacci number of a previously calculated one (in a previous call for a previous bit of the flag), we simply load the result of the calculation and XOR the current output bit with the one we already saved.

The solution is implemented in the script below. All modifications done to the original function are commented.
Full script : here

/*
Souhail Hammou
HXP CTF 2017 - Fibonacci 100 pts
Writeup : https://rce4fun.blogspot.com/2017/11/hxp-ctf-2017-fibonacci-reversing-100.html
*/
#include <stdio.h>
#include <stdlib.h>
#define _BYTE unsigned char
#define BYTEn(x, n) (*((_BYTE*)&(x)+n))
#define BYTE1(x) BYTEn(x, 1)
unsigned int saved_res_for_v[264][2];
unsigned int v_index;
unsigned int fibonacci_solve(int number, _BYTE *pbool);
int main()
{
unsigned int i,j;
unsigned char barray[] = {0x49,0x7E,0x07,0xE2,0x9C,0xCC,0xC9,0x2B,0xFC,0x34,0x04,0x6F,0x4E,0x12,0x00,0x50,0xA9,0x02,0x3A,0xBA,0xC2,0x8E,0x41,0x99,0x98,0xF5,0x8D,0x51,0x4D,0xA6,0xC6,0x43,0x12};
unsigned char flag_char;
unsigned int counter = 0; //RBX
for ( i = 0; i < sizeof(barray) ; i++)
{
flag_char = barray[i];
for ( j = 0 ; j < 8 ; j++)
{
_BYTE b = 0;
unsigned int fibo = fibonacci_solve(counter+j,&b);
//save data for this value
saved_res_for_v[counter+j][0] = b; //save bit
saved_res_for_v[counter+j][1] = fibo; //save fibonacci result
flag_char ^= b << j;
}
counter += 8;
printf("%c",flag_char);
}
}
unsigned int fibonacci_solve(int number, _BYTE *pbool)
{
_BYTE *v2;
unsigned int v3;
unsigned int result;
unsigned int v5;
unsigned int v6;
unsigned int v7;
unsigned int xor_res;
v2 = pbool;
if ( number )
{
if ( number == 1 )
{
result = fibonacci_solve(0, pbool);
v5 = result - ((result >> 1) & 0x55555555);
v6 = ((result - ((result >> 1) & 0x55555555)) >> 2) & 0x33333333;
}
else
{
//if (number - 2) is saved
if ( saved_res_for_v[number-2][1] != 0 )
{
//update b to reflect the value
*pbool ^= saved_res_for_v[number-2][0];
//don't recalculate, load the fibo value
v3 = saved_res_for_v[number-2][1];
}
else
{
//calculate the value normally
v3 = fibonacci_solve(number - 2, pbool);
}
//if number-1 is saved
if ( saved_res_for_v[number-1][1] != 0 )
{
//update b to reflect the value
*pbool ^= saved_res_for_v[number-1][0];
//don't recalculate, load the fibo value
result = v3 + saved_res_for_v[number-1][1];
}
else
{
//calculate the value normally
result = v3 + fibonacci_solve(number - 1, pbool);
}
v5 = result - ((result >> 1) & 0x55555555);
v6 = ((result - ((result >> 1) & 0x55555555)) >> 2) & 0x33333333;
}
v7 = v6 + (v5 & 0x33333333) + ((v6 + (v5 & 0x33333333)) >> 4);
*v2 ^= ((BYTE1(v7) & 0xF) + (v7 & 0xF) + (unsigned __int8)((((v7 >> 8) & 0xF0F0F) + (v7 & 0xF0F0F0F)) >> 16)) & 1;
}
else
{
*pbool ^= 1u;
result = 1;
}
return result;
}
view raw solve.c hosted with ❤ by GitHub

We immediately get the flag when we run the program.

 

Monday, June 5, 2017

Exploring Virtual Address Descriptors under Windows 10

This blog post is about my personal attempt to superficially list VAD types under Windows 10. It all started when I was wondering, out of sheer curiosity, if there's any way to determine the VAD type (MMVAD_SHORT or MMVAD) other than by looking at the pool tag preceding the structure. In order to do that, I had to list all VAD types, do some reverse engineering, and then draw a table describing what I've been able to find.
You can view the full document by clicking here 



From the table above it is possible to deduce the VAD structure type from both the VadType and PrivateMemory flags.

VadType flag
PrivateMemory flag
Type
0
0
MMVAD
0
1
MMVAD_SHORT
1
1
MMVAD
2
0
MMVAD
3
1
MMVAD_ENCLAVE

To test it out, I wrote a kernel driver that prints the deduced VAD type for each node of calc.exe. It also prints the pool tag so we can check the result.


And that's all for this article.
You can follow me on Twitter : here

Monday, May 22, 2017

RCTF 2017 - Crackme 714 pts Writeup


Crackme 714 pts (9 solves) :

The crackme is an MFC application :

 

We can locate the routine of interest by setting a breakpoint on GetWindowTextW. Keep in mind that the input is in Unicode.
Later on, we find that the program generates two distinct blocks of values. These are generated from hard-coded values independently from the user input, so they're always the same. We call the first one : static_block1 and the second static_block2.
Then, there's the call to the encrypt function which takes static_block1 as an argument.
 
The encrypted block will then be XORed with static_block2.
We also find a reference to the encrypted valid key here, which we can extract easily during runtime :

 
The loop above performs a double-word by double-word comparison of the encrypted user input with the encrypted valid key that already came with the crackme.

In order to solve the challenge we need to reverse engineer the encrypt function and do the exact reverse. We also don't have to forget the XOR that is done with static_block2. For that matter, we supply to the decrypt function (the one we need to write) encrypted_valid_key XOR static_block2.

The script below has my implementation of the decrypt function, it outputs the key to flag.txt :

/*
RCTF - 2017
Author : SOUHAIL HAMMOU
Crackme 714 pts (9 solves)
Description :
============
Please submit the flag like RCTF{flag}
https://static2017.teamrois.cn/re_b889ffe02c96c38274f76c67f8a1ddf3/crackme_63074830f0b1b6b4fff6ad910bea34fc.zip
*/
#include <stdio.h>
#include <stdlib.h>
#define __ROL__(x, y) _rotl(x, y)
inline unsigned int __ROL4__(unsigned int value, int count) { return __ROL__((unsigned int)value, count); }
inline unsigned int __ROR4__(unsigned int value, int count) { return __ROL__((unsigned int)value, -count); }
typedef unsigned char BYTE;
typedef unsigned int DWORD;
void _decrypt(DWORD* input)
{
BYTE static_block_2[] = {
0xCA,0xF4,0x60,0x16,0xDC,0xB7,0x2F,0x71,0x3D,0xEA,0x7D,0xF3,0xC8,0x2E,0xA8,0x17,
0x2E,0x3A,0x47,0xCE,0x33,0x85,0xE3,0x10,0x4F,0xB1,0x85,0x5D,0x87,0xB0,0x05,0x84,
0xFC,0xCA,0x8F,0x89,0xF4,0x36,0x17,0xBD,0xE1,0x8D,0xF1,0x89,0x5B,0x3F,0x37,0xE4,
0x27,0x26,0xEF,0xD1,0x6A,0xDF,0xB1,0xDA,0xF0,0x63,0x04,0x8F,0xD7,0x79,0x50,0x22,
0x4D,0xD4,0x66,0xD0,0xE1,0xE0,0xCC,0x12,0x3A,0xFB,0xE5,0x04,0xDA,0x24,0x62,0xCA,
0x33,0xC5,0xD4,0x95,0x4D,0xA8,0x81,0x81,0x44,0x20,0xF3,0xBA,0x39,0x77,0x26,0x44,
0xF7,0xBD,0x61,0x6B,0x6C,0x84,0x1D,0x25,0x38,0x73,0x5F,0x76,0xD1,0xE0,0x2B,0x57,
0x1B,0xC2,0x9B,0x36,0xF6,0x23,0xBA,0x82,0xAA,0xF6,0x72,0x4F,0xD3,0xB1,0x67,0x18
};
DWORD prev_1, prev_2;
int ROL_1_res;
int ROL_2_res;
int ROL_4_res;
int ROL_3_res;
input = input + 1;
for (unsigned int n = 0; n < 4; n++)
{
*input += 0x5BF76637;
input[2] += 0x4748DA7A;
int i = 0;
int j = 4;
do
{
//Deduce the ROL values
ROL_2_res = __ROL4__(*input * (2 * *input + 1), 8);
ROL_4_res = ROL_2_res ^ *(input - 1);
ROL_1_res = __ROL4__(input[2] * (2 * input[2] + 1), 8);
ROL_3_res = input[1] ^ ROL_1_res;
///// The values of the 4 double-words in the next loop :
///// next loop's *(ptr-1) is current *ptr
///// next loop's ptr[1] is ptr[2]
///// next loop's ptr[2] is calculated below via interm
///// next loop's *ptr is calculated below via interm2
DWORD interm = __ROR4__(ROL_4_res, 32 - (ROL_1_res & 0x1F));
prev_2 = interm + ((DWORD*)static_block_2)[i / 4];
DWORD interm2 = __ROR4__(ROL_3_res, 32 - (ROL_2_res & 0x1F));
prev_1 = interm2 + ((DWORD*)static_block_2)[j >> 2];
//now that we have all the necessary values, write them to the array for the next loop
*(input - 1) = *input;
*input = prev_1;
input[1] = input[2];
input[2] = prev_2;
i += 8;
j += 8;
} while (j <= 124);
*(input - 1) += 0x7FAF076D;
input[1] -= 0x642805B4;
input += 4;
}
}
int main()
{
FILE* fp = _wfopen(L"flag.txt", L"w, ccs=UTF-16LE");
/*
Result of :
AF C7 E8 A9 8F 75 75 5E 51 3D 9D 5E AD 88 8E 1D <=
7F 78 F2 70 E3 E1 12 9F AF 11 8E D9 2F DF 54 DB <= static block 1
C1 11 1B D5 12 B2 9E 82 1B 12 0B 86 44 60 26 B8 <=
4D 9C 20 73 AF A3 C2 AB B8 17 DC EB 22 C3 4D E6 <=
XOR
7d 34 13 f9 89 07 0e ff a1 3b f8 fa 30 14 9a b7 <=
00 48 29 78 f6 32 3c 31 db 1a c1 c6 98 2e 7f ba <= XORed encrypted key
4c e9 27 81 ce fe 23 5d 06 42 7c 50 d3 07 e4 56 <=
99 ea a6 d2 36 9d a5 52 af 75 82 cc da 14 59 d9 <=
= key's value after the _encrypt routine (sub_013A1040); i.e : it's ready for decryption now
*/
BYTE key[] = {
0x7d,0x34,0x13,0xf9,0x89,0x07,0x0e,0xff,0xa1,0x3b,0xf8,0xfa,0x30,0x14,0x9a,0xb7,
0x00,0x48,0x29,0x78,0xf6,0x32,0x3c,0x31,0xdb,0x1a,0xc1,0xc6,0x98,0x2e,0x7f,0xba,
0x4c,0xe9,0x27,0x81,0xce,0xfe,0x23,0x5d,0x06,0x42,0x7c,0x50,0xd3,0x07,0xe4,0x56,
0x99,0xea,0xa6,0xd2,0x36,0x9d,0xa5,0x52,0xaf,0x75,0x82,0xcc,0xda,0x14,0x59,0xd9
};
_decrypt((DWORD*)key);
//
// Decrypted key : 你好,欢迎进入rctf2017,希望你学到东西。
//
fwprintf(fp,(wchar_t*)key);
}
All we need to do now, is provide the decrypted key and the flag will be displayed.

The flag is : RCTF{rtf2017crackmebyw31y1l1n9}

See you again soon
Follow me on Twitter : here