NahamCon CTF 2024 On this page https://ctftime.org/event/2364
CHALL’S SOLVED# Reverse Engineering# IPromise# Overview Author: @soups71
Description: Instead of making the next IPhone, I made this challenge. I do make a truthful promise though…
IPromise
First rev category challenge, let’s try to execute the binary first
$ ./IPromise
I promise that I do some decryption! You just have to find out where. Writing code shouldn' t be necessary ; )
All we have to do is decompile the binary according to the output that has been given. But when I first checked using gdb, I saw that it had a lot of functions.
$ gdb ./IPromise
gef➤ info functions
All defined functions:
Non-debugging symbols:
0x0000000000401000 _init
0x0000000000401040 puts@plt
0x0000000000401050 main
0x0000000000401065 decryptIPromise
0x00000000004010d0 _start
0x0000000000401100 _dl_relocate_static_pie
0x0000000000401110 deregister_tm_clones
0x0000000000401140 register_tm_clones
0x0000000000401180 __do_global_dtors_aux
0x00000000004011b0 frame_dummy
0x00000000004011b6 KeyExpansion
0x000000000040124e AddRoundKey
0x0000000000401285 xtime
0x0000000000401294 Cipher
0x0000000000401405 InvCipher
0x0000000000401633 AES_init_ctx
0x000000000040163c AES_init_ctx_iv
0x000000000040165d AES_ctx_set_iv
0x000000000040166c AES_ECB_encrypt
0x0000000000401678 AES_ECB_decrypt
0x0000000000401684 AES_CBC_encrypt_buffer
0x00000000004016e4 AES_CBC_decrypt_buffer
0x0000000000401740 AES_CTR_xcrypt_buffer
0x00000000004017c8 _fini
Here’s the decompile of decryptIPromise
function
__int64 decryptIPromise ()
{
__int128 v1 ; // [rsp+0h] [rbp-D8h] BYREF
char v2 [ 200 ]; // [rsp+10h] [rbp-C8h] BYREF
v1 = xmmword_4022B0 ;
AES_init_ctx ( v2 , & v1 );
AES_ECB_decrypt ( v2 , & encrypted );
AES_ECB_decrypt ( v2 , & unk_404050 );
return AES_ECB_decrypt ( v2 , & unk_404060 );
}
This function initializes an AES context with a key, and then performs AES decryption in ECB mode on three blocks of data.
Then, the key initialization is performed by AES_init_ctx
function calls KeyExpansion
to expand the provided key.
char __fastcall AES_init_ctx ( _BYTE * a1 , __int64 a2 )
{
return KeyExpansion ( a1 , a2 );
}
The KeyExpansion
function expands the key for AES encryption/decryption, this function takes the key provided (a2) and generates the expanded key used in AES operations.
char __fastcall KeyExpansion ( _BYTE * a1 , __int64 a2 )
{
// Key expansion logic
}
The AES_ECB_decrypt
function is responsible for the actual decryption
__int64 __fastcall AES_ECB_decrypt ( __int64 a1 , __int64 a2 )
{
return InvCipher ( a2 , a1 );
}
The InvCipher
function performs the inverse AES cipher operation
__int64 __fastcall InvCipher ( __int64 a1 , __int64 a2 )
{
// Inverse AES cipher logic
}
We need to extract the necessary values such as xmmword_4022B0
& encrypted
,
let’s dissasembly with gdb > set breakpoint to decryptIPromise
> run
$ gdb ./IPromise
gef➤ b decryptIPromise
Breakpoint 1 at 0x401065
gef➤ run
First of all extract the xmmword_4022B0
memory to find the AES key,
gef➤ x/16xb 0x4022B0
0x4022b0: 0x2b 0x7e 0x15 0x16 0x28 0xae 0xd2 0xa6
0x4022b8: 0xab 0xf7 0x15 0x88 0x09 0xcf 0x4f 0x3c
Next, inspect the encrypted
(0x404040
) memory to find the encrypted data.
gef➤ x/64xb & encrypted
0x404040 <encrypted>: 0x0f 0x5f 0xa3 0xb9 0x09 0x23 0x71 0x50
0x404048 <encrypted+8>: 0xbb 0x4f 0x6f 0x6b 0x88 0x1d 0x96 0xc2
0x404050 <encrypted+16>: 0x80 0x29 0x5f 0xe0 0x71 0x90 0x64 0xa6
0x404058 <encrypted+24>: 0xe5 0x35 0x86 0x64 0xb4 0x0c 0xcb 0xb4
0x404060 <encrypted+32>: 0xd8 0x23 0x6f 0x12 0x02 0x54 0xe2 0x0b
0x404068 <encrypted+40>: 0x94 0x83 0xde 0x09 0xf4 0x3e 0x6d 0x24
0x404070 <completed.0>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x404078: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
All values have been successfully obtained, here are the final solver
1 from Crypto.Cipher import AES
2
3 key = bytes ([ 0x2b , 0x7e , 0x15 , 0x16 , 0x28 , 0xae , 0xd2 , 0xa6 , 0xab , 0xf7 , 0x15 , 0x88 , 0x09 , 0xcf , 0x4f , 0x3c ])
4 enc = bytes ([ 0x0f , 0x5f , 0xa3 , 0xb9 , 0x09 , 0x23 , 0x71 , 0x50 , 0xbb , 0x4f , 0x6f , 0x6b , 0x88 , 0x1d , 0x96 , 0xc2 , 0x80 , 0x29 , 0x5f , 0xe0 , 0x71 , 0x90 , 0x64 , 0xa6 , 0xe5 , 0x35 , 0x86 , 0x64 , 0xb4 , 0x0c , 0xcb , 0xb4 , 0xd8 , 0x23 , 0x6f , 0x12 , 0x02 , 0x54 , 0xe2 , 0x0b , 0x94 , 0x83 , 0xde , 0x09 , 0xf4 , 0x3e , 0x6d , 0x24 ])
5
6 # Initialize AES cipher in ECB mode with the key
7 cipher = AES . new ( key , AES . MODE_ECB )
8
9 dec = cipher . decrypt ( enc )
10 print ( dec . strip ())
$ python3 solver.py
b'flag{d41d8cd98f00b204e9800998ecf8427e}'
FLAG flag{d41d8cd98f00b204e9800998ecf8427e}
Taylor’s First Swift# Overview Author: @alden
Description: OMG did you hear that they named a programming language after Taylor Swift?
taylor
Second rev category challenge, based on the challenge description it looks like it uses the Swift or maybe Objective-C ? ok, whatever..
The xorEncrypt
function takes two arguments, both of which are arrays of bytes. It enumerates over the first array, and for each element, it performs an XOR operation with an element from the second array at a corresponding index. The index is calculated as the current index modulo the length of the second array. The result of the XOR operation is then base64 encoded and returned.
__int64 __fastcall xorEncrypt ( _ : _ :)( __int64 a1 , __int64 a2 )
{
__int64 v2 ; // x0
__int64 result ; // x0
__int64 v4 ; // x21
__int64 v5 ; // x1
__int64 v6 ; // x0
__int64 v7 ; // [xsp+8h] [xbp-C8h]
__int64 v8 ; // [xsp+10h] [xbp-C0h]
__int64 v9 ; // [xsp+20h] [xbp-B0h]
__int64 v10 ; // [xsp+30h] [xbp-A0h]
__int64 v11 ; // [xsp+38h] [xbp-98h]
__int64 v13 ; // [xsp+50h] [xbp-80h]
__int64 v14 ; // [xsp+58h] [xbp-78h]
char v15 [ 16 ]; // [xsp+60h] [xbp-70h] BYREF
__int64 v16 ; // [xsp+70h] [xbp-60h]
__int64 v17 ; // [xsp+78h] [xbp-58h] BYREF
__int64 v18 ; // [xsp+80h] [xbp-50h]
__int64 v19 [ 5 ]; // [xsp+88h] [xbp-48h] BYREF
v18 = 0LL ;
v19 [ 4 ] = a1 ;
v19 [ 3 ] = a2 ;
v19 [ 1 ] = a1 ;
v10 = __swift_instantiateConcreteTypeFromMangledName ( & _sSays5UInt8VGMD );
v11 = lazy protocol witness table accessor for type [ UInt8 ] and conformance [ A ]();
Sequence . enumerated ()( v10 );
v19 [ 0 ] = v19 [ 2 ];
swift_bridgeObjectRetain ( a2 );
v16 = a2 ;
v13 = __swift_instantiateConcreteTypeFromMangledName ( & _ss18EnumeratedSequenceVySays5UInt8VGGMD );
v2 = lazy protocol witness table accessor for type EnumeratedSequence < [ UInt8 ] > and conformance EnumeratedSequence < A > ();
result = Sequence . map < A > ( _ :)( partial apply for closure # 1 in xorEncrypt ( _ : _ :), v15 , v13 , & dword_100000014 , v2 );
v14 = result ;
if ( v4 )
{
__break ( 1u );
}
else
{
swift_bridgeObjectRelease ( a2 );
outlined destroy of EnumeratedSequence < [ UInt8 ] > ( v19 );
v18 = v14 ;
swift_bridgeObjectRetain ( v14 );
v17 = v14 ;
v7 = Data . init < A > ( _ :)( & v17 , v10 , v11 );
v8 = v5 ;
v6 = default argument 0 of Data . base64EncodedString ( options :)();
v9 = Data . base64EncodedString ( options :)( v6 , v7 , v8 );
outlined consume of Data . _Representation ( v7 , v8 );
swift_bridgeObjectRelease ( v14 );
return v9 ;
}
return result ;
}
The flagCheck
function takes the user’s input and compares it to a hardcoded string. It first converts the input and the hardcoded string to arrays of bytes. Then it calls the xorEncrypt function with these two arrays as arguments. The result is compared to another hardcoded base64 string.
__int64 __fastcall flagCheck ( _ :)( __int64 a1 , __int64 a2 )
{
__int64 v2 ; // x1
__int64 v3 ; // x1
__int64 v4 ; // x0
_BYTE * v5 ; // x1
__int64 v6 ; // x1
__int64 v7 ; // x1
__int64 v8 ; // x0
void * v9 ; // x1
__int64 v13 ; // [xsp+18h] [xbp-B8h]
__int64 v14 ; // [xsp+20h] [xbp-B0h]
__int64 v15 ; // [xsp+28h] [xbp-A8h]
__int64 v16 ; // [xsp+50h] [xbp-80h]
__int64 v17 ; // [xsp+58h] [xbp-78h]
__int64 v18 ; // [xsp+60h] [xbp-70h]
__int64 v19 ; // [xsp+68h] [xbp-68h]
char v20 ; // [xsp+74h] [xbp-5Ch]
__int64 v21 [ 4 ]; // [xsp+78h] [xbp-58h] BYREF
__int64 v22 [ 3 ]; // [xsp+98h] [xbp-38h] BYREF
__int64 v23 [ 4 ]; // [xsp+B0h] [xbp-20h] BYREF
v23 [ 2 ] = a1 ;
v23 [ 3 ] = a2 ;
v23 [ 0 ] = (( __int64 ( * )( void )) String . utf8 . getter )();
v23 [ 1 ] = v2 ;
lazy protocol witness table accessor for type String . UTF8View and conformance String . UTF8View ();
v19 = Array . init < A > ( _ :)( v23 , & dword_100000014 , & dword_100000004 );
v22 [ 2 ] = v19 ;
v22 [ 0 ] = String . utf8 . getter ( a1 , a2 );
v22 [ 1 ] = v3 ;
v14 = Array . init < A > ( _ :)( v22 , & dword_100000014 , & dword_100000004 );
v4 = _allocateUninitializedArray < A > ( _ :)( 9LL , & dword_100000014 );
* v5 = 115 ;
v5 [ 1 ] = 119 ;
v5 [ 2 ] = 105 ;
v5 [ 3 ] = 102 ;
v5 [ 4 ] = 116 ;
v5 [ 5 ] = 105 ;
v5 [ 6 ] = 101 ;
v5 [ 7 ] = 115 ;
v5 [ 8 ] = 33 ;
v13 = _finalizeUninitializedArray < A > ( _ :)( v4 , & dword_100000014 );
v15 = xorEncrypt ( _ : _ :)( v14 , v13 );
v18 = v6 ;
swift_bridgeObjectRelease ( v13 );
swift_bridgeObjectRelease ( v14 );
v21 [ 2 ] = v15 ;
v21 [ 3 ] = v18 ;
v21 [ 0 ] = String . utf8 . getter ( v15 , v18 );
v21 [ 1 ] = v7 ;
v17 = Array . init < A > ( _ :)( v21 , & dword_100000014 , & dword_100000004 );
v8 = _allocateUninitializedArray < A > ( _ :)( 52LL , & dword_100000014 );
qmemcpy ( v9 , "FRsIAQ8PVBUVEREIVERbBkURFkUIBxVQVkAYFxJfV0FYVkIVQgo=" , 52 );
v16 = _finalizeUninitializedArray < A > ( _ :)( v8 , & dword_100000014 );
v20 = static Array < A > . == infix ( _ : _ :)( v17 );
swift_bridgeObjectRelease ( v16 );
swift_bridgeObjectRelease ( v17 );
swift_bridgeObjectRelease ( v18 );
swift_bridgeObjectRelease ( v19 );
return v20 & 1 ;
Final solver
1 import base64
2
3 # Hardcoded string in ASCII values
4 hardcoded_string = [ 115 , 119 , 105 , 102 , 116 , 105 , 101 , 115 , 33 ] # "swifties!"
5
6 hardcoded_base64 = "FRsIAQ8PVBUVEREIVERbBkURFkUIBxVQVkAYFxJfV0FYVkIVQgo="
7 decoded_base64 = base64 . b64decode ( hardcoded_base64 )
8
9 result = '' . join ( chr ( decoded_base64 [ i ] ^ hardcoded_string [ i % len ( hardcoded_string )]) for i in range ( len ( decoded_base64 )))
10 print ( result )
$ python3 solver.py
flag{ f1f4bfa202c60e2aaa9339de61513141}
FLAG flag{f1f4bfa202c60e2aaa9339de61513141}
Ring Cycle 1 - Basics# Overview Author: @awesome10billion
Description: Let us start with a simple one and see if you can break into this vault.
NOTE, the plaintext that you provide the binary should be readable English. Reverse engineer the binary to understand what it really does… patching the binary will ultimately give you the wrong answer.
basics.txt basics
Next, the first of rev category challenge group and the last rev category I solved.
let’s decompile first
int __cdecl main ( int argc , const char ** argv , const char ** envp )
{
size_t v4 ; // rax
int i ; // [rsp+4h] [rbp-6Ch]
FILE * stream ; // [rsp+8h] [rbp-68h]
__int64 nmemb ; // [rsp+10h] [rbp-60h]
void * ptr ; // [rsp+18h] [rbp-58h]
char v9 [ 16 ]; // [rsp+20h] [rbp-50h] BYREF
char s [ 56 ]; // [rsp+30h] [rbp-40h] BYREF
unsigned __int64 v11 ; // [rsp+68h] [rbp-8h]
v11 = __readfsqword ( 0x28u );
init ( argc , argv , envp );
printf ( "What is the passphrase of the vault? \n > " );
fgets ( s , 50 , stdin );
if ( ( unsigned __int8 ) check ( s ) )
{
stream = fopen ( "basics.txt" , "r" );
if ( ! stream )
return - 1 ;
fseek ( stream , 0LL , 2 );
nmemb = ftell ( stream );
fseek ( stream , 0LL , 0 );
ptr = calloc ( nmemb , 1uLL );
if ( ! ptr )
return - 1 ;
fread ( ptr , 1uLL , nmemb , stream );
fclose ( stream );
printf (( const char * ) ptr );
v4 = strlen ( s );
MD5 ( s , v4 , v9 );
printf ( "flag{" );
for ( i = 0 ; i <= 15 ; ++ i )
printf ( "%02x" , ( unsigned __int8 ) v9 [ i ]);
puts ( "}" );
}
else
{
puts ( "Wrong passphrase!" );
}
return 0 ;
}
_BOOL8 __fastcall check ( __int64 a1 )
{
char v2 ; // [rsp+11h] [rbp-8Fh]
char v3 ; // [rsp+12h] [rbp-8Eh]
char v4 ; // [rsp+13h] [rbp-8Dh]
int i ; // [rsp+14h] [rbp-8Ch]
int j ; // [rsp+18h] [rbp-88h]
int k ; // [rsp+1Ch] [rbp-84h]
char s1 [ 64 ]; // [rsp+20h] [rbp-80h] BYREF
char s2 [ 56 ]; // [rsp+60h] [rbp-40h] BYREF
unsigned __int64 v10 ; // [rsp+98h] [rbp-8h]
v10 = __readfsqword ( 0x28u );
for ( i = 0 ; i <= 24 ; ++ i )
{
v4 = * ( _BYTE * )( i + a1 );
s1 [ i ] = * ( _BYTE * )( 50 - i - 1LL + a1 );
s1 [ 49 - i ] = v4 ;
}
for ( j = 0 ; j <= 49 ; ++ j )
{
v3 = s1 [ j ];
s1 [ j ] = s1 [ j + 1 ];
s1 [ j + 1 ] = v3 ;
}
s1 [ 49 ] = 0 ;
for ( k = 0 ; k <= 47 ; k += 2 )
{
v2 = s1 [ k ];
s1 [ k ] = s1 [ k + 1 ];
s1 [ k + 1 ] = v2 ;
}
strcpy ( s2 , "eyrnou jngkiaccre af suryot arsto tdyea rre aouY" );
return strcmp ( s1 , s2 ) == 0 ;
}
The check
function is used to validate the passphrase input. If the passphrase is correct, the main
function will print out the flag.
The check
function does the following operations on the input string:
Swaps every pair of characters. Shifts all characters one position to the right. Reverses the string. Final solver,
1 import subprocess
2
3 def reverse_check ( s ):
4 s = list ( s )
5 for k in range ( 0 , 47 , 2 ):
6 s [ k ], s [ k + 1 ] = s [ k + 1 ], s [ k ]
7 s = '' . join ( s )
8 s = s [ 1 :] + s [ 0 ]
9 s = s [:: - 1 ]
10 s = s [ 1 :] + s [ 0 ]
11 return s
12
13 passphrase = reverse_check ( "eyrnou jngkiaccre af suryot arsto tdyea rre aouY" )
14 print ( passphrase )
15
16 subprocess . run ([ './basics' ], input = passphrase , text = True )
$ python3 solver.py
You are ready to start your safe cracking journey
What is the passphrase of the vault?
> *******************************************************************************
| | | |
_________| ________________.= "" _; = .______________| _____________________| _______
| | ,-"_,="" `" = .| |
| ___________________| __"=._o`" -._ ` "=.______________|___________________
| `" = ._o` "=._ _`" = ._ |
_________| _____________________:= ._o "=._." _.-= "'" = .__________________| _______
| | __.--" , ; `" = ._o." ,-""" -._ ". |
|___________________|_._" ,. .` ` `` , ` "-._" -._ ". '__|___________________
| |o`" = ._` , "` `; ." . , "-._" -._; ; |
_________| ___________| ; ` -.o` "=._; ." ` '`."\` . "-._ /_______________|_______
| | |o; `"-.o`"=._`` ' ` " ,__.--o; |
|___________________|_| ; (#) `-.o `" = .` _.--"_o.-; ;___|___________________
____/______/______/___|o;._ " ` ".o|o_.--" ; o; ____/______/______/____
/______/______/______/_"=._o--._ ; | ; ; ;/______/______/______/_
____/______/______/______/__" = ._o--._ ; o| o; _._; o; ____/______/______/____
/______/______/______/______/____"=._o._; | ;_.--" o.--"_/______/______/______/_
____/______/______/______/______/_____" = .o| o_.--"" ___/______/______/______/____
/______/______/______/______/______/______/______/______/______/______/[ TomekK]
*******************************************************************************
flag{ 8562e979f1f754537a4e872cc20a73e8}
FLAG flag{8562e979f1f754537a4e872cc20a73e8}
Web Exploitation# iDoor# Overview Author: @awesome10billion
Description: It’s Apple’s latest innovation, the “iDoor!” … well, it is basically the Ring Doorbell camera, but the iDoor offers a web-based browser to monitor your camera, and super secure using ultimate cryptography with even SHA256 hashing algorithms to protect customers! Don’t even think about snooping on other people’s cameras!!
http://challenge.nahamcon.com:31646
The service are already down, so here’s my solver
1 import re
2 import hashlib
3 import requests
4 from bs4 import BeautifulSoup
5
6 base_url = "http://challenge.nahamcon.com:31646/"
7
8 for i in range ( 20 ):
9 sha256_hash = hashlib . sha256 ( str ( i ) . encode ()) . hexdigest ()
10 url = base_url + sha256_hash
11 response = requests . get ( url )
12 soup = BeautifulSoup ( response . text , 'html.parser' )
13 card_body = soup . find ( 'div' , class_ = 'card-body' )
14 print ( f "URL: { url } " )
15 print ( f "Response: \n { card_body } \n " )
16
17 flag = re . findall ( r 'flag{.*}' , str ( card_body ))
18
19 if flag :
20 print ( flag [ 0 ])
21 break
The script bruteforces a specific URL by replacing parts of the URL with a SHA256 hash of numbers 0 to 19. For each generated URL, the script performs an HTTP GET request and looks for the flag{.*}
pattern in the HTML response.
If the pattern is found (which means the flag has been found), the script prints the flag and stops. Otherwise, the script will continue to the next iteration until all numbers in the range have been tried. In this case, the script encountered the flag on the first iteration (number 0), so the script stopped after the first iteration.
$ python3 solver.py
URL: http://challenge.nahamcon.com:31646/5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9
Response:
<div class = "card-body" >
<div style = "text-align: center; vertical-align: middle; height: 100%;" >
<h1><b><code>flag{ 770a058a80a9bca0a87c3e2ebe1ee9b2} </code></b></h1>
</div>
</div>
flag{ 770a058a80a9bca0a87c3e2ebe1ee9b2}
FLAG flag{770a058a80a9bca0a87c3e2ebe1ee9b2}
All About Robots# Overview Author: @JohnHammond
Description: Oh wow! Now you can learn all about robots, with our latest web service, All About Robots!!
http://challenge.nahamcon.com:30998/
The service are already down, so here’s my solver
1 import re
2 import hashlib
3 import requests
4 from bs4 import BeautifulSoup
5
6 base_url = "http://challenge.nahamcon.com:30998"
7
8 robotstxt = "/robots.txt"
9 endpoint = "/open_the_pod_bay_doors_hal_and_give_me_the_flag.html"
10
11 response = requests . get ( base_url + endpoint )
12
13 flag = re . findall ( r 'flag{.*}' , response . text )
14
15 print ( flag [ 0 ])
FLAG flag{3f19b983c1de42bd49af1a237d7e57b9}
Warmups# QRRRRRRRR# Overview Author: @JohnHammond
Description: Wait a second, they made QR codes longer!?!
qrrrrrrrr.png
They provide a long qr image
But the athor which John Hammond has discussed this in his YouTube video,
We can scan it using QRQR - QR Code® Reader
FLAG flag{a44557e380e3baae9c21c738664c6142}
EICAR# Overview Author: @JohnHammond
Description: What is the MD5 hash of this file?
Wrap the hexadecimal value in the flag{ prefix and { suffix to match the standard flag format.
Note, your antivirus engine might flag this file – don’t worry, I promise it’s not malware :)
eicar
Here we go, just wrap it to flag{.*}
$ md5sum eicar
44d88612fea8a8f36de82e1278abb02f eicar
FLAG flag{44d88612fea8a8f36de82e1278abb02f}
That’s Not My Base# Overview Author: @JohnHammond
Description: Everyone knows about Base64 , but do you know about this one?
(Remember, the flag format starts with flag{!)
F#S<YRXdP0Fd=,%J4c$Ph7XV(gF/*]%C4B<qlH+%3xGHo)\
Just decode from base92 F#S<YRXdP0Fd=,%J4c$Ph7XV(gF/*]%C4B<qlH+%3xGHo)\
Decoded from base92
FLAG flag{784454a9509196a33dba242c423c057a}
Uriel# Overview Author: @JohnHammond
Description: Everyone knows about Base64 , but do you know about this one?
(Remember, the flag format starts with flag{!)
F#S<YRXdP0Fd=,%J4c$Ph7XV(gF/*]%C4B<qlH+%3xGHo)\
Just decode from url decode 2 times
1 from urllib.parse import unquote
2
3 url = " %25% 36 %36% 25 %36% 63 %25% 36 %31% 25 %36% 37 %25% 37 %62% 25 %33% 38 %25% 36 %35% 25 %36% 36 %25% 36 %35% 25 %36% 32 %25% 33 %36% 25 %33% 36 %25% 36 %31% 25 %33% 37 %25% 33 %31% 25 %33% 39 %25% 36 %32% 25 %33% 37 %25% 33 %35% 25 %36% 31 %25% 33 %34% 25 %36% 32 %25% 33 %37% 25 %36% 33 %25% 33 %36% 25 %33% 33 %25% 33 %34% 25 %36% 34 %25% 33 %38% 25 %33% 38 %25% 33 %35% 25 %33% 37 %25% 33 %38% 25 %33% 38 %25% 36 %34% 25 %36% 36 %25% 36 %33% 25 %37% 64"
4
5 # Perform unquoting twice
6 print ( unquote ( unquote ( url )))
FLAG flag{8efeb66a719b75a4b7c634d885788dfc}
Twine# Overview Author: @JohnHammond#6971
Description: Google tells me that twine means: “strong thread or string consisting of two or more strands of hemp, cotton, or nylon twisted together.”
twine.jpg
Just strings it
$ strings twine.jpg | grep -o 'flag{[^}]*}'
flag{ 4ac54e3ba5f8f09049f3ad62403abb25}
FLAG flag{4ac54e3ba5f8f09049f3ad62403abb25}