investigation_encoded_2
Problem
We have recovered a binary and 1 file: image01. See what you can make of it. Its also found in /problems/investigation-encoded-2_6_74ebdbfd3962c221df51c8ce5141b275 on the shell server. NOTE: The flag is not in the normal picoCTF{XXX} format.
Solution
Running the binary produces:
Error: file ./flag.txt not found
Lets create a flag with
echo picoctf > flag.txt
and try running again which just causes aSegmentation fault
Decompile the binary file using Ghidra (cheat sheet):
undefined8 main(void) { long lVar1; size_t sVar2; undefined4 local_18; int local_14; FILE *local_10; badChars = '\0'; local_10 = fopen("flag.txt","r"); if (local_10 == (FILE *)0x0) { fwrite("Error: file ./flag.txt not found\n",1,0x21,stderr); /* WARNING: Subroutine does not return */ exit(1); } flag_size = 0; fseek(local_10,0,2); lVar1 = ftell(local_10); flag_size = (int)lVar1; fseek(local_10,0,0); login(); if (0xfffe < flag_size) { fwrite("Error, file bigger than 65535\n",1,0x1e,stderr); /* WARNING: Subroutine does not return */ exit(1); } flag = malloc((long)flag_size); sVar2 = fread(flag,1,(long)flag_size,local_10); local_14 = (int)sVar2; if (local_14 < 1) { /* WARNING: Subroutine does not return */ exit(0); } local_18 = 0; flag_index = &local_18; output = fopen("output","w"); buffChar = 0; remain = 7; fclose(local_10); encode(); fclose(output); if (badChars == '\x01') { fwrite("Invalid Characters in flag.txt\n./output is corrupted\n",1,0x35,stderr); } else { fwrite("I\'m Done, check file ./output\n",1,0x1e,stderr); } return 0; }
encode()
function decompiled:void encode(void) { byte bVar1; int iVar2; int local_10; char local_9; while (*flag_index < flag_size) { local_9 = lower((ulong)(uint)(int)*(char *)(*flag_index + flag)); if (local_9 == ' ') { local_9 = -0x7b; } else { if (('/' < local_9) && (local_9 < ':')) { local_9 = local_9 + 'K'; } } local_9 = local_9 + -0x61; if ((local_9 < '\0') || ('$' < local_9)) { badChars = 1; } if (local_9 != '$') { iVar2 = ((int)local_9 + 0x12) % 0x24; bVar1 = (byte)(iVar2 >> 0x1f); local_9 = ((byte)iVar2 ^ bVar1) - bVar1; } local_10 = *(int *)(indexTable + (long)(int)local_9 * 4); iVar2 = *(int *)(indexTable + (long)((int)local_9 + 1) * 4); while (local_10 < iVar2) { getValue(); save(); local_10 = local_10 + 1; } *flag_index = *flag_index + 1; } while (remain != 7) { save(0); } return; }
getValue()
function decompiled:ulong getValue(int param_1) { byte bVar1; int iVar2; iVar2 = param_1; if (param_1 < 0) { iVar2 = param_1 + 7; } bVar1 = (byte)(param_1 >> 0x37); return (ulong)((int)(uint)(byte)secret[iVar2 >> 3] >> (7 - (((char)param_1 + (bVar1 >> 5) & 7) - (bVar1 >> 5)) & 0x1f) & 1); }
save()
function decompiled:void save(byte param_1) { buffChar = buffChar | param_1; if (remain == 0) { remain = 7; fputc((int)(char)buffChar,output); buffChar = '\0'; } else { buffChar = buffChar * '\x02'; remain = remain + -1; } return; }
Differences from previous challenge,
investigation_encoded_1
:The
matrix
array was replaced withindexTable
The digits 0-9 (inclusive) are valid for input (the flag can include numbers now). The while loop through the flag adds
K
(75) if the current character is between '\' and '$', which are the ascii values for the digits 0-9. AddingK
is important becausea
(97) is subtracted from each character, regardless whether its a number or not. 48 is the ascii value for 0. Adding 75 yields 123 and subtracting the 97 gives 26. This means that from 0-25 are the letters a-z and then starting at 26 are the numbers 0-9. Since 97 is always subtracted, only a-z,{
,|
,}
,~
, and any exceptions (0-9 and space) are accepted. However, the characters{
,|
,}
,~
would get the same encoding as0
,1
,2
, and3
, so they can be ignored.The program performs some kind of manipulation on the characters before using them as array indices:
iVar2 = ((int)local_9 + 0x12) % 0x24; bVar1 = (byte)(iVar2 >> 0x1f); local_9 = ((byte)iVar2 ^ bVar1) - bVar1;
There is a
login
function which will crash the program (This was the cause of theSegmentation fault
earlier). It can be skipped over during debugging.
Get
indexTable
andsecret
usingradare2
:[0x00000ae0]> bf obj.secret [0x00000ae0]> pc @ obj.secret #define _BUFFER_SIZE 71 const uint8_t buffer[_BUFFER_SIZE] = { 0x8b, 0xaa, 0x2e, 0xee, 0xe8, 0xbb, 0xae, 0x8e, 0xbb, 0xae, 0x3a, 0xee, 0x8e, 0xee, 0xa8, 0xee, 0xae, 0xe3, 0xaa, 0xe3, 0xae, 0xbb, 0x8b, 0xae, 0xb8, 0xea, 0xae, 0x2e, 0xba, 0x2e, 0xae, 0x8a, 0xee, 0xa3, 0xab, 0xa3, 0xbb, 0xbb, 0x8b, 0xbb, 0xb8, 0xae, 0xee, 0x2a, 0xee, 0x2e, 0x2a, 0xb8, 0xaa, 0x8e, 0xaa, 0x3b, 0xaa, 0x3b, 0xba, 0x8e, 0xa8, 0xeb, 0xa3, 0xa8, 0xaa, 0x28, 0xbb, 0xb8, 0xae, 0x2a, 0xe2, 0xee, 0x3a, 0xb8, 0x00 }; [0x00000ae0]> bf obj.indexTable [0x00000ae0]> pcw @ obj.indexTable #define _BUFFER_SIZE 38 const uint32_t buffer[_BUFFER_SIZE] = { 0x00000000U, 0x00000004U, 0x00000012U, 0x00000028U, 0x0000003cU, 0x00000052U, 0x00000064U, 0x00000078U, 0x0000008eU, 0x0000009eU, 0x000000b4U, 0x000000c8U, 0x000000daU, 0x000000eaU, 0x000000fcU, 0x0000010eU, 0x0000011eU, 0x00000134U, 0x00000148U, 0x0000015aU, 0x0000016aU, 0x00000172U, 0x00000180U, 0x0000018cU, 0x0000019aU, 0x000001aaU, 0x000001bcU, 0x000001c8U, 0x000001d6U, 0x000001e0U, 0x000001eaU, 0x000001f0U, 0x00000200U, 0x0000020aU, 0x00000216U, 0x00000222U, 0x00000230U, 0x00000234U }; [0x00000ae0]> pc @ obj.indexTable #define _BUFFER_SIZE 152 const uint8_t buffer[_BUFFER_SIZE] = { 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x0e, 0x01, 0x00, 0x00, 0x1e, 0x01, 0x00, 0x00, 0x34, 0x01, 0x00, 0x00, 0x48, 0x01, 0x00, 0x00, 0x5a, 0x01, 0x00, 0x00, 0x6a, 0x01, 0x00, 0x00, 0x72, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x8c, 0x01, 0x00, 0x00, 0x9a, 0x01, 0x00, 0x00, 0xaa, 0x01, 0x00, 0x00, 0xbc, 0x01, 0x00, 0x00, 0xc8, 0x01, 0x00, 0x00, 0xd6, 0x01, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0xea, 0x01, 0x00, 0x00, 0xf0, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x0a, 0x02, 0x00, 0x00, 0x16, 0x02, 0x00, 0x00, 0x22, 0x02, 0x00, 0x00, 0x30, 0x02, 0x00, 0x00, 0x34, 0x02, 0x00, 0x00 };
Find the sequences that correspond with each letter using the decode.py script:
a: 101011101110111000 b: 1010101110111000 c: 10111000 d: 10101010111000 e: 101010101000 f: 11101010101000 g: 1110111010101000 h: 111011101110101000 i: 111010101000 j: 11101011101000 k: 1110101000 l: 1010101000 m: 101000 n: 1011101110111000 o: 1010111000 p: 101010111000 q: 101110111000 r: 11101010111000 s: 1000 t: 10111010101000 u: 1011101110111011101000 v: 10111011101011101000 w: 1110101110111010111000 x: 111010111011101000 y: 11101110111010101000 z: 1110111010101110111000 0: 1110101010111000 1: 1110101110101110111000 2: 10111010111010111000 3: 111010101010111000 4: 1011101011101000 5: 101110101011101000 6: 101011101110101000 7: 1110101011101000 8: 1110111011101110111000 9: 10111011101110111000 Python dictionary: {'101011101110111000': 'a', '1010101110111000': 'b', '10111000': 'c', '10101010111000': 'd', '101010101000': 'e', '11101010101000': 'f', '1110111010101000': 'g', '111011101110101000': 'h', '111010101000': 'i', '11101011101000': 'j', '1110101000': 'k', '1010101000': 'l', '101000': 'm', '1011101110111000': 'n', '1010111000': 'o', '101010111000': 'p', '101110111000': 'q', '11101010111000': 'r', '1000': 's', '10111010101000': 't', '1011101110111011101000': 'u', '10111011101011101000': 'v', '1110101110111010111000': 'w', '111010111011101000': 'x', '11101110111010101000': 'y', '1110111010101110111000': 'z', '1110101010111000': '0', '1110101110101110111000': '1', '10111010111010111000': '2', '111010101010111000': '3', '1011101011101000': '4', '101110101011101000': '5', '101011101110101000': '6', '1110101011101000': '7', '1110111011101110111000': '8', '10111011101110111000': '9'}
Unlike the previous script in
investigation_encoded_1
, this script makes use of the C word (4 byte) representation of thematrix
/indexTable
variable instead of the 1 byte representation. To handle this, the* 4
was removed fromindex
andend
. The* 4
effectively selected every 4th value, so removing that and only keeping every 4th value is an alternative way to solve this challenge instead of manually updating the 1 byte representation ofindexTable
as was done in the previous challenge.
Flag
t1m3f1i3500000000000098a9a51
Last updated
Was this helpful?