CTF Writeup

Defcon 2023 Qualifier - kkkkklik writeup

LittleDev0617 2023. 5. 31. 17:51

I solved this problem with my team "Budaejjigae".

 

TL; DR

더보기

1. Encryption Algorithm

 when click a picture 1337 times, we can encrypt "flag{this_is_a_fake_flag_find_the_real_o" with Blowfish Encryption. and key is set by user input.

2. Encrypted flag

 when click the picture 1333337 times,

can get encrypted flag.

3. Get Key

In case 133337, there are many function call by dynamic function table. there are no msgbox.

this routine is draw a key at below of the background bitmap. 

set counter variable to 133337 and run at least one time the routine.

use spy++ to get handle of the kkkkklik window, and run below c code.

#include <windows.h>

int main()
{	
	//HWND hwnd = FindWindow(0, "kkkkklik");
	HWND hwnd = (HWND)0x0AE08EA;
	MoveWindow(hwnd,0, -500, 1000, 1800, 1);
}

Yes, key is AKAM1337

 decrypt at http://sladex.org/blowfish.js/

flag : flag{vb6_and_blowfish_fun_from_the_old_days}

 

When you run the provided exe file, a strange picture appears.

 

Alright, let's open it with IDA.

 

We can know this program was made with Visual Basic.

Next, put the program into VB Decompiler to gather additional information.

We locate the pic_Click callback function. Returning to IDA, we navigate to pic_Click.

there are switch-case statement.

There are cases for 1337, 133337, and 1333337 , each containing obfuscated code.

With dynamic anlysis, we can determine that the variable 'v6' used in the switch statement represents the click count.

so when we click the picture 1337, 133337, 1333337 times, something going on.

 

now let's open the ollydbg.

set breakpoint on 78064D and click, run, click, run, click, then [ESI+34h] will be 2.

set [ESI+34] to 1337 and patch the counter updating code

set 1337 - 1

and continue.

some MsgBox with a input box will appear.

Let's enter 'asdf' as the key and click a button.

Ok.

 

set counter to 1333337 and run.

 

Now, we need to analyze the encryption algorithm.

 

In 1337 we can see some function call to encrypt with my key.

Call sub_783EA0 - sub_784220 - sub_78FB60 - Done

we can know 3EA0 is Encryption func and 78FB60 is b64 encoding func.

 

now we need to analysis 3EA0 function.

I spent approximately 4~5 hours analyzing and implementing the 3EA0 function into python code.

and dynamic analysis is very very helpful to know what the function do.

 

so.... it's too long and hard to detail analysis so jump to python code.

 

from base64 import b64encode, b64decode


def encrypt(key):
    if key == b'':
        key = b'\xAB'
    DEBUG = False
    kk = bytes(map(lambda x: int(x, 16), 'A1 72 4F E8 FA 10 D3 49 07 92 69 DF 6D 6B 00 CF 0B 20 79 68 F9 29 EF E5 B1 E2 5E C4 A0 74 3E 20 CF 39 58 89 5E 0B A0 F4 E6 7E 24 72 45 14 99 F8 9E 31 DC 0C F4 48 0C 05 9C CD F4 F3 3E 11 37 79 F0 CD 66 5E 32 E3 09 45'.split()))
    dword_7910C8 = [int.from_bytes(kk[i:i+4], 'little') for i in range(0, len(kk), 4)]    
    dword_7910E4 = [0] * 1024
    with open('./dump', 'rb') as f:
        tmp = f.read()
        dword_7910E4 = [int.from_bytes(tmp[i:i+4], 'little') for i in range(0, len(tmp), 4)]

    def sub_783A40(x):
        return x ^ 0xcc701829
    
    def sub_78FA70(a, b):
        return (a + b) & 0xFFFFFFFF

    def sub_783AC0(x):
        tmp = list(reversed([(x >> 8 * i) & 0xFF for i in range(4)]))
        v4 = sub_783A40(dword_7910E4[tmp[0] * 4])
        v8 = sub_783A40(dword_7910E4[tmp[1] * 4 + 1])
        v5 = sub_78FA70(v4, v8)
        v2 = sub_783A40(dword_7910E4[tmp[2] * 4 + 2])
        v5 ^= v2
        v4 = sub_783A40(dword_7910E4[tmp[3] * 4 + 3])
        return sub_78FA70(v5, v4)

    
    def sub_783BC0(_a, _b, _c):        
        a, b, c = _a, _b, _c
        tmp = 0
        for i in range(16):
            b ^= sub_783A40(dword_7910C8[i])
            tmp = sub_783AC0(b) ^ c            
            c = b
            b = tmp

        b = c
        c = tmp ^ sub_783A40(dword_7910C8[16])        
        b ^= sub_783A40(dword_7910C8[17])

        return (a,b,c)

    for i in range(18):
        v6 = 0
        for j in range(4):                        
            v6 |= key[(i*4 + j) % len(key)]
            v6 <<= 8
        v6 >>= 8
        dword_7910C8[i] ^= v6
        
    b, c = (0,0)
    for i in range(0, 18, 2):
        a,b,c = sub_783BC0(0,b,c)
        dword_7910C8[i] = sub_783A40(b)
        dword_7910C8[i+1] = sub_783A40(c)


    result = b''.join([dword_7910C8[i].to_bytes(4, 'little') for i in range(16)])

    print(result.hex())

    for i in range(4):
        for k in range(0, 256, 2):
            a,b,c = sub_783BC0(a,b,c)
            dword_7910E4[4*k + i] = sub_783A40(b)
            dword_7910E4[4*(k+1) + i] = sub_783A40(c)

    result = b''.join([dword_7910E4[i].to_bytes(4, 'little') for i in range(16)])

    print(result.hex())

    DEBUG = True
    fake_flag = b'flag{this_is_a_fake_flag_find_the_real_o'
    fake_flag = [int.from_bytes(fake_flag[i:i+4], 'big') for i in range(0, len(fake_flag), 4)]
    result = []
    for i in range(0,len(fake_flag),2):
        b, c = fake_flag[i], fake_flag[i+1]
        a, b, c = sub_783BC0(0, b, c)
        result.append(b.to_bytes(4, 'big') + c.to_bytes(4, 'big'))

    result = b''.join(result)
    print(len(result))
    print(b64encode(result))
        
    
encrypt(b'asdf')

here is short above encrypt func.

1. there are two constant table A and B. 

    int A[18], int B[256][4]

2. xor each bytes of A and input key.

3. update A and B table with A, B table.

4. encrypt "flag{this_is_a_fake_flag_find_the_real_o" (plain text)

 

B table is 4 sboxes. 

I panicked after writing the above python code because of thinking that "How to make decrypt() func?".

the input key greatly influences the entire encryption process, so hard to reverse it.

 

that time, the team member give me the pretty cool information.

OMG. there are 4 sboxes!!!!!

http://sladex.org/blowfish.js/

 

blowfish.js encrypt/decrypt online

blowfish.js encrypt/decrypt online Standalone Blowfish library from Dojo Toolkit: blowfish.js Data to encrypt or decrypt Key Cipher mode ECB CBC PCBC CFB OFB CTR Enumeration for various cipher modes. Output type Base64 Hex String Raw Enumeration for input

sladex.org

 test some input, key-output in above site, we can decide this encryption is Blowfish.

 

 

Ok, now we only have to know "key"!

 

and there are 133337 case that we don't see.

there are no MsgBox or any other output function calls.

I once again think a lot of time about this part.

"Is it forensic? Can I exoprt the background image?"

Using binwalk, I can extract the bitmap file.

long long long picture

By watching bitmap's header, we can deduce this file's size is intended. why? why it have long long space?

I expected that key is written bottom of the picture or some stegano, but I was fail.

 

I open VB decompiler and watch API section.

What? there are CreatePen, CreatePatternBrush .. functions that related with Drawing something.

then we can guess unknown routine in 133337 case is drawing key at bottom space.

let's check it.

 

first, set the counter to 133337 - 1 and run the program. 

second, call MoveWindow to resize kkkklick window. 

 

#include <windows.h>

int main()
{	
	//HWND hwnd = FindWindow(0, "kkkkklik");
	HWND hwnd = (HWND)0x0AE08EA;
	MoveWindow(hwnd,0, -500, 1000, 1800, 1);
}

YEAAAAAAAAAAAAAAAH 

Key is AKAM1337

 

flag{vb6_and_blowfish_fun_from_the_old_days}

 

Thanks to Budaejjigae team!

 

 첫 데프콘 예선 참가였는데, 돌아보니 그렇게까지 어렵지 않았던 이 문제였지만 플래그를 얻었을 때 지금까지 얻은 플래그 중 가장 설레고 기뻤던 것 같다. 

 포항공대의 PLUS 팀원분들과 teamh4c 등등 굉장한 고수분들 40명 정도와 함께 참가했고, 24위를 기록하였다.

이 문제 풀고 집중력이 다해서 다른 문제에 도움을 못 주었지만, 거의 20개 가량의 문제들을 꾸준히 분석하고 푸는 팀원분들이 매우 멋있었던 것 같다.

'CTF Writeup' 카테고리의 다른 글

zer0ptsCTF 2023 - fvm Writeup  (1) 2023.07.18
2023 Codegate UNIV division writeup  (0) 2023.06.18
2022 Incognito CTF Writeup  (0) 2023.03.26
2023 ACSC CTF Writeup - warmup + ngo  (0) 2023.02.26
2022 Iris CTF  (0) 2023.01.08