Writeup: FlareOn 2020: 006 - codeit
1. TLDR
2. Dane wejściowe
Plik z zadaniem znajduje się tutaj. Hasło: flare.
Przedmiot zadania stanowi plik codeit.exe
stanowiący skompilowany skrypt. Z treści zadania wiadomo również, że flaga została ukryta jako kod QR.
3. Inspekcja pliku codeit.exe
Zweryfikowałem plik narzędziem file
:
$ file codeit.exe
codeit.exe: PE32 executable (GUI) Intel 80386, for MS Windows, UPX compressed
Plik został spakowany popularnym narzędziem UPX.
Uruchomienie programu powodowało pojawienie się okna aplikacji:
Aplikacja pozwalała na zakodowanie podanego tekstu do kodu QR:
4. Odpakowanie pliku
Korzystając z narzędzia UPX, odpakowałem plik:
$ upx -d -o codeit_unpacked.exe codeit.exe
Ultimate Packer for eXecutables
Copyright (C) 1996 - 2020
UPX git-d7ba31+ Markus Oberhumer, Laszlo Molnar & John Reiser Jan 23rd 2020
File size Ratio Format Name
-------------------- ------ ----------- -----------
963584 <- 481280 49.95% win32/pe codeit_unpacked.exe
Unpacked 1 file.
Zweryfikowałem odpakowany plik narzędziem file
:
file codeit_unpacked.exe
codeit_unpacked.exe: PE32 executable (GUI) Intel 80386, for MS Windows
Następnie przeanalizowałem ciągi znaków występujące w pliku wykonywalnym. Wynik analizy jednoznacznie wskazał na fakt, że program został utworzony przez kompilację skryptu AutoIt:
$ strings codeit_unpacked.exe| grep -i autoit
This is a third-party compiled AutoIt script.
5. Dekompilacja
Zdekompilowałem plik codeit.exe
do skryptu AutoIt wykorzystując do tego narzędzie Exe2Aut. W wyniku dekompilacji zostały odzyskane pliki:
codeit.au3 - zawierający zobfuskowany skrypt AutoIt
qr_encoder.dll - biblioteka implementująca obsługę kodów QR
$ file qr_encoder.dll
qr_encoder.dll: PE32 executable (DLL) (GUI) Intel 80386, for MS Windows
- sprite.bmp - bitmapa
file sprite.bmp
sprite.bmp: PC bitmap, Windows 3.x format, 299 x 299 x 24, image size 269102, resolution 3779 x 3779 px/m, cbSize 269156, bits offset 54
Otworzyłem plik sprite.bmp w hexedytorze. Po 54-bajtowym nagłówku bitmapy, zidentyfikowałem powtarzające sie bajty 0xFF oraz 0xFE. Zmiana najmniej znaczącego bitu wskazywała na ukrycie informacji:
6. Deobfuskacja
Skrypt AutoIt zdeobfuskowałem wykonując napisany do tego celu skrypt. Pozostało wykonać jeszcze ręczną analizę: ponazywać funkcje i zmienne oraz zrozumieć kod. Z powodu uciążliwości tego procesu, w części 7 przedstawiłem analizę istotnej części ostatecznej wersji skryptu.
7. Analiza skryptu
Flaga została zapisana w postaci kodu QR, zaszyfrowana i zahardkodowana w kodzie funkcji areyzotafnf()
.
Sposób działania tej funkcji prezentuje poniższy diagram:
Odczytana nazwa NetBIOS komputera była przekształcana przez funkcje mieszającą. Następnie zostawał obliczany skrót SHA-256 z wyniku tego przekształcenia. Konkatenacja stałego nagłówka i obliczonego skrótu stanowiła klucz zgodny CryptoApi, który służył do odszyfrowania kodu QR.
Zatem w celu odczytania flagi, trzeba było odgadnąć nazwę maszyny, z której wyznaczany był klucz.
Jak pozyskać oczekiwaną nazwę maszyny? Słabością całego rozwiązania okazała się funkcja MixComputerName()
:
Func MixComputerName(ByRef $computer_name_raw)
Local $randomized_sprite_bmp = InstallSelectedFile(14)
Local $hFile_sprite = CreateFileForReading($randomized_sprite_bmp)
If $hFile_sprite <> -1 Then
Local $hFile_sprite_size = GetFileSize($hFile_sprite)
If $hFile_sprite_size <> -1 AND DllStructGetSize($computer_name_raw) < $hFile_sprite_size - 54 Then
Local $in_memory_sprite_bmp = DllStructCreate("struct;byte[" & $hFile_sprite_size & "];endstruct")
Local $is_success = ReadFile($hFile_sprite, $in_memory_sprite_bmp)
If $is_success <> -1 Then
Local $key = DllStructCreate("struct;byte[54];byte[" & $hFile_sprite_size - 54 & "];endstruct", DllStructGetPtr($in_memory_sprite_bmp))
Local $sprite_offset = 1
Local $mixed_computer_name = ""
For $index = 1 To DllStructGetSize($computer_name_raw)
Local $character = Number(DllStructGetData($computer_name_raw, 1, $index))
For $shift_counter = 6 To 0 Step -1
$character += BitShift(BitAND(Number(DllStructGetData($key, 2, $sprite_offset)), 1), -1 * $shift_counter)
$sprite_offset += 1
Next
$mixed_computer_name &= Chr(BitShift($character, 1) + BitShift(BitAND($character, 1), -7))
Next
DllStructSetData($computer_name_raw, 1, $mixed_computer_name)
EndIf
EndIf
CloseHandle($hFile_sprite)
EndIf
DeleteFileA($randomized_sprite_bmp)
EndFunc
Mieszanie nazwy komputera odbywało się poprzez dodawanie do każdego znaku kolejnego znaku odczytanego 7 z najmniej znaczących bitów pochodzących z pliku sprite.bmp, a następnie rotację o jeden bit w prawo.
Należało więc postawić pytanie o zawartość ciągu zbudowanego 7-bitowych znaków. Ostatni bajt 0xFE był 90 w sekwencji, należało więc odczytać ⌈90/7⌉ bajtów. W tym celu można było zmodyfikować skrypt lub napisać kawałek kodu np. w pythonie:
#! /usr/bin/python3
covert_message = [0xFF, 0xFF, 0xFE, 0xFE, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, \
0xFE, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFE, 0xFE, \
0xFE, 0xFF, 0xFF, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFF, 0xFF, 0xFE, \
0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFE, 0xFE, 0xFF, \
0xFF, 0xFE, 0xFE, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFE, 0xFE, 0xFE, \
0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0xFF, \
0xFF, 0xFE, 0xFE, 0xFE, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, \
0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0xFF, 0xFE, 0xFF, 0xFF, \
0xFF, 0xFE, 0xFE, 0xFF]
def main():
message = "";
character = 0
for i in range(len(covert_message)):
character |= ((covert_message[i] & 0x01) << (6 - (i % 7)))
if i % 7 == 6:
message += chr(character)
character = 0
print(message)
if __name__ == "__main__":
main()
Uruchomienie skryptu pozwoliło na odczytanie ciągu znaków, który stanowił oczekiwaną nazwę komputera:
aut01tfan1999
8. Odczytanie flagi
Wystarczyło więc zmodyfikować skrypt tak, żeby podstawiana była zawsze oczekiwana nazwa komputera:
oraz uruchomić skrypt AutoIt:
$ "C:\Program Files (x86)\AutoIt3\AutoIt3.exe" "codeit_debug.au3"
Po kliknięciu na przycisk Can haz code?
wygenerowany został kod QR:
Po skorzystaniu z aparatu telefonu lub dowolnego innego narzędzia (np. CyberChef) można było odczytać zdekodowaną flagę:
L00ks_L1k3_Y0u_D1dnt_Run_Aut0_Tim3_0n_Th1s_0ne!@flare-on.com