Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
yohanes committed Sep 22, 2015
0 parents commit 84161ec
Show file tree
Hide file tree
Showing 40 changed files with 7,855 additions and 0 deletions.
8 changes: 8 additions & 0 deletions README.md
@@ -0,0 +1,8 @@

This is my write-up repository for single person CTF.

See [Play4Fun](http://p4f.github.io) for write-ups from my current team and [Rentjong](https://github.com/rentjongteam/) for my Indonesian team.

Current write-up:

* [Flare On 2015](flare-2015)
17 changes: 17 additions & 0 deletions flare-2015/README.md
@@ -0,0 +1,17 @@
# Flare-On 2015


Content

* [Challenge 1](flare1)
* [Challenge 2](flare2)
* [Challenge 3](flare3)
* [Challenge 4](flare4)
* [Challenge 5](flare5)
* [Challenge 6](flare6)
* [Challenge 7](flare7)
* [Challenge 8](flare8)
* [Challenge 9](flare9)
* [Challenge 10](flare10)
* [Challenge 11](flare11)

13 changes: 13 additions & 0 deletions flare-2015/flare1/README.md
@@ -0,0 +1,13 @@
# Challenge 1

This is so trivial. You can see the complete tutorial to solve it in the webinar (which I didn't see until I finished all the challenges). The only thing I want to show here is that I solved this using objdump, I didn't have access to Windows when I downloaded the first challenge:

objdump -p -s -d i_am_happy_you_are_to_playing_the_flareon_challenge.exe

I know its not easy to read it, and it didn't occur to me to use online disassembler, but with a bit of concentration we can immediatelly see that `push $0x4020f2` refers to the string `"Let's start out easy\r\nEnter the password>"`. Address `$0x402158` will contain the result of `ReadFile`, and it will be compared with block at `0x402140` after xoring it with fixed key `0x7d`. So basically this is just XOR encryption, 24 characters (`"cmp $0x18,%ecx"`).

objdump -s --start-address=0x402140 --stop=0x402158 i_am_happy_you_are_to_playing_the_flareon_challenge.exe

And this one line of python is enough to solve it:

print "".join([chr(ord(x) ^ 0x7d) for x in "1f08131304220e114d0d183d1b111c0f18501213531e1210".decode("hex")])
9 changes: 9 additions & 0 deletions flare-2015/flare10/README.md
@@ -0,0 +1,9 @@
# Challenge 10

This file is an `AutoIt.exe`. It can be extracted using: Exe2Aut.exe. The autoit script doesn't do anything meaningful except for installing the driver and calling IOCTL, so I went to look at the driver. The driver contains many functions, mostly junk, except for parts where it writes a byte to certain memory location.

I am still not sure how to separate the junk instructions, so I just wander around the code to see if there are something that is clear and doesn't contain junk. One thing pops up: a number that is used as constant to TEA encryption algorithm. The input for the algorithm is a memory location, and a fixed key.

I am too lazy to setup an environment to debug the code, so I just used Ida's xref function to trace one by one the bytes written to the memory location that is decrypted. I traced the first 8 bytes, tried to decrypt it, and got a plaintext `uncondit`, it looks right, so I just continued to the next bytes, until I got the string: `unconditional_conditions@flare-o`, and just stops there.


47 changes: 47 additions & 0 deletions flare-2015/flare10/flare10.c
@@ -0,0 +1,47 @@
#include <stdio.h>

unsigned int TEA(int *a1, int *a2) //
{
unsigned int result; // eax@4
unsigned int v3; // [sp+0h] [bp-24h]@1
signed int v4; // [sp+14h] [bp-10h]@1
unsigned int v5; // [sp+18h] [bp-Ch]@1
unsigned int i; // [sp+20h] [bp-4h]@1

v3 = *a1;
v5 = a1[1];
v4 = 0xC6EF3720;
for ( i = 0; i < 32; ++i )
{
v5 -= (a2[3] + (v3 >> 5)) ^ (v4 + v3) ^ (a2[2] + 16 * v3);
v3 -= (a2[1] + (v5 >> 5)) ^ (v4 + v5) ^ (*a2 + 16 * v5);
v4 += 0x61C88647;
}
*a1 = v3;
result = v5;
a1[1] = v5;
return result;
}

void decrypt(char *ax)
{
int key[] = {0x33323130,0x37363534,0x42413938,0x46454443, 0};
TEA((int *)ax, key);
printf("%s", ax);
}

int main()
{

char ax1[] = "V\x7F\xDC\xFA\xAA\x27\x99\xC4\x00";
decrypt(ax1);
char ax2[] ="\x6c\x7c\xfc\x92\x61\x61\x47\x1a\x00";
decrypt(ax2);
char ax3[] ="\x19\xb9\x63\xfd\x0c\xf2\xb6\x20\x00";
decrypt(ax3);
char ax4[] = "\xC0\x2D\x5C\xFD\xD9\x71\x54\x96\x00";
decrypt(ax4);
printf("\n");

return 0;
}
43 changes: 43 additions & 0 deletions flare-2015/flare11/README.md
@@ -0,0 +1,43 @@
# Challenge 11

Note: This write up is not as complete as I want it to be. May be I will update it someday.

Reading the easy part of the code: this app tries to decrypt a resource file to "secret.jpg". Like the challenge #4, it reads a single argument from command line, but this time brute force doesn't work. Even given the correct parameter, the program just hangs.

I tried to extract the encryption code so I can just call it, but there are too many functions being called. I tried reimplementing it, but its too complicated.

For the first part: finding the correct parameter, I just patched the binary, so it will exit with esi as the return value (`call exit(esi)`) after the first MD5 comparation. When `esi` is less than 0, it means I got the correct parameter.

Here is the patch:

fc CryptoGraph.exe CryptoGraph-patched.exe
Comparing files CryptoGraph.exe and CRYPTOGRAPH-PATCHED.EXE
00000B29: 85 56
00000B2A: FF E8
00000B2B: 74 2E
00000B2C: 09 4F
00000B2D: 57 00
00000B2E: E8 00

For the second part, I finally understood that the loop is mean to create decryption key for the decryption. I also saw a bit counting algorithm being used. I have a theory that you don't need the full 32 iteration to generate the bytes necessary to decrypt the resource inside the file.

First I need to be able to control the loop count, and also the return value of the bit counting mechanism. Then I wrote a python brute forcer. In another window I executed: "file *jpg", until I got:

c-9.exe.jpg: JPEG image data, JFIF standard 1.01, resolution (DPI), density 72x72, segment length 16, baseline, precision 8, 416x416, frames 3

The patch for the second part:

fc CryptoGraph.exe CryptoGraph2.exe
Comparing files CryptoGraph.exe and CRYPTOGRAPH2.EXE
00000CC2: 20 02
000011A6: FF 90
000011A7: 75 90
000011A8: 10 90
000011AC: FF 90
000011AD: 75 90
000011AE: C0 90
000011AF: E8 B8
000011B0: AC 02
000011B1: FD 00
000011B2: FF 00
000011B3: FF 00
20 changes: 20 additions & 0 deletions flare-2015/flare11/brute.py
@@ -0,0 +1,20 @@
import os

with open("CryptoGraph2.exe", "rb") as f:
r = f.read()

n = 0xcc2 #patch loop count
r = r[:n] + chr(0xA) + r[n+1:]

n = 0x11B0
for i in range(0, 64):
c = chr(i)
#patch bit count
new = r[:n] + c + r[n+1:]
name = "c-%d.exe" % i
with open(name, "wb") as f:
f.write(new)
os.system(name + " 205")
os.rename("secret.jpg", name + ".jpg");


9 changes: 9 additions & 0 deletions flare-2015/flare11/stage1.py
@@ -0,0 +1,9 @@
import os
for i in range(0, 1000):
print "i = ", i
n = os.system("CryptoGraph-patched.exe %d" % i)
if (n<=100):
print "-------", n
with open("result.xt", "a+") as f:
f.write(str(i)+"\n");

6 changes: 6 additions & 0 deletions flare-2015/flare2/Makefile
@@ -0,0 +1,6 @@
flare2: flare2.c func.o
gcc -m32 flare2.c func.o -o flare2

func.o: func.asm
nasm -f elf func.asm

57 changes: 57 additions & 0 deletions flare-2015/flare2/README.md
@@ -0,0 +1,57 @@
# Challenge 2

This is also a small and easy challenge, and I just want to show how I used `objdump` + `nasm` to solve the problem without knowing the exact algorithm. I looked at the encryption part a little bit and I don't remember exactly the bits on the x86 flags i.e: I don't know which flags are set by `sahf` instruction, and I don't want to spend time to check and implement it. Still on Linux, I used objdump to cut the encryption part. Added this header:

---
BITS 32
global func


section .text
func:
-----

And changed the addresses to labels.

nasm -f elf func.asm
gcc -m32 flare.c func.o

And wrote this short code:

```C
#include <stdio.h>

extern int func(char *s1, char *s2, int len);

int main()
{
char *a1 = "\xaf\xaa\xad\xeb\xae\xaa\xec\xa4\xba\xaf\xae"
"\xaa\x8a\xc0\xa7\xb0\xbc\x9a\xba\xa5\xa5\xba"
"\xaf\xb8\x9d\xb8\xf9\xae\x9d\xab\xb4\xbc\xb6"
"\xb3\x90\x9a\xa8"; //edi (compare)
char inp[] = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00"; //esi (input)
int i = 0;
int ch = 0;
int count = 0x25;

for (i=0; i < 37; i++) {
for (ch=0; ch < 255; ch++) {
inp[i] = ch;
int n = func(a1, inp, 0x25);
int idx = (n & 0xff00)>>8;
if (idx!=count) {
printf("%c", ch);
count = idx;
break;
}
}
}
printf("\n");

}
```
So in this case: I don't need to know how the algorithm works, just by understanding a little bit of the main loop, I can just brute force it.
27 changes: 27 additions & 0 deletions flare-2015/flare2/flare2.c
@@ -0,0 +1,27 @@
#include <stdio.h>

extern int func(char *s1, char *s2, int len);

int main()
{
char *a1 = "\xaf\xaa\xad\xeb\xae\xaa\xec\xa4\xba\xaf\xae\xaa\x8a\xc0\xa7\xb0\xbc\x9a\xba\xa5\xa5\xba\xaf\xb8\x9d\xb8\xf9\xae\x9d\xab\xb4\xbc\xb6\xb3\x90\x9a\xa8"; //edi (compare)
char inp[] = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; //esi (input)
int i = 0;
int ch = 0;
int count = 0x25;

for (i=0; i < 37; i++) {
for (ch=0; ch < 255; ch++) {
inp[i] = ch;
int n = func(a1, inp, 0x25);
int idx = (n & 0xff00)>>8;
if (idx!=count) {
printf("%c", ch);
count = idx;
break;
}
}
}
printf("\n");

}
57 changes: 57 additions & 0 deletions flare-2015/flare2/func.asm
@@ -0,0 +1,57 @@
BITS 32
global func


section .text
func:
push ebp
mov ebp,esp
sub esp,0x0
push edi
push esi
;; make it 0
xor eax, eax
xor ebx,ebx
mov ecx,0x25
cmp DWORD [ebp+0x10],ecx
jl .done
mov esi,DWORD [ebp+0xc]
mov edi,DWORD [ebp+0x8]
lea edi,[edi+ecx*1-0x1]
.loop1:
mov dx,bx
and dx,0x3
mov ax,0x1c7
push eax
sahf
lodsb
pushf
xor al,BYTE [esp+0x4]
xchg dl,cl
rol ah,cl
popf
adc al,ah
xchg dl,cl ; dl contains loop counter
xor edx,edx
and eax,0xff
add bx,ax
scasb
jne .wrong
cmovne cx,dx
pop eax
jecxz .done
sub edi,0x2
loop .loop1
jmp .done
xor eax,eax
jmp .done
.wrong:
shl ecx, 8
or eax, ecx
.done:
pop esi
pop edi
mov esp,ebp
pop ebp
ret
34 changes: 34 additions & 0 deletions flare-2015/flare3/README.md
@@ -0,0 +1,34 @@
# Challenge 3

This is a huge (12 Mb) binary. I made a guess that this file must be a special binary. I looked at some clues on using strings, and it shows:

mstruct
mpyi_os_path
mpyi_archive
mpyi_importers
s_pyi_bootstrap
spyi_carchive
selfie
bMicrosoft.VC90.CRT.manifest
bmsvcr90.dll
bmsvcp90.dll
bmsvcm90.dll
bpython27.dll
bselect.pyd
bunicodedata.pyd
bPySide.QtCore.pyd
b_hashlib.pyd
bbz2.pyd
b_ssl.pyd
bPySide.QtGui.pyd
b_socket.pyd
bpyside-python2.7.dll
bshiboken-python2.7.dll
bQtCore4.dll
bQtGui4.dll
belfie.exe.manifest
python27.dll

Ok so this was built using Python. The is a hint from the string about pyi_archive, so i tried extracting it using `pyi-archive_viewer`, and it works. There is file inside it named `"elfie"`, it reminded me of [a CTF problem where they embed an ELF file inside an ELF and called it selfie](https://github.com/smokeleeteveryday/CTF_WRITEUPS/tree/master/2015/ASISCTF/reversing/selfie). So I immediatelly looked at the file named `elfie`. Its an obfuscated python file, but can be easily decoded using `print`.

In the result file, I saw the string `reversed ("moc.no-eralf@...")`, and knew immediatelly that this is the string that I am looking for. We just need to reverse it: `"".join(list(reversed("moc.no-eralf@...")))`
9 changes: 9 additions & 0 deletions flare-2015/flare4/README.md
@@ -0,0 +1,9 @@
# Challenge 4

Using `strings`, I can see that this file is packed using UPX (you can see the string `UPX0`, `UPX1`), so I decided to postpone this problem until I got an access to a Windows machine. I tried to decompress it using `upx` command line, but it doesn't run in Windows 10. I thought that this must have used a modified compressor.

Anyway, I just run this inside OllyDbg. The interesting thing is: it reads from a command line, and converts the string from command line it to single byte int. 256 is very small key space, so I just brute force this without knowing what exactly this file does.

for /l %x in (0, 1, 255) do youPecks.exe %x >> result.txt

And you can see the answer in one of the lines. Reading others writeup, it seems that this is time dependant.
8 changes: 8 additions & 0 deletions flare-2015/flare5/Makefile
@@ -0,0 +1,8 @@
flare5 : flare5.c enc.o
gcc -m32 flare5.c enc.o -o flare5

flare5test : flare5test.c enc.o
gcc -m32 flare5test.c enc.o -o flare5test

enc.o: enc.asm
nasm -f elf enc.asm

0 comments on commit 84161ec

Please sign in to comment.