Playing Hacks and Stuffs!


We’re given a ssh instance to connect to after loggin i looked back at the description and saw its referring to automating task on linux

And that means likely cron jobs
So i checked the cron jobs and got the flag

Flag: picoCTF{Sch3DUL7NG_T45K3_L1NUX_d83baed1}

We’re given an address which looks like a btc address and we’re asked to submit the malware name
So i just paste the address on google then saw this

The second link looks promising then i checked it Link

Below the news on the cyberattack shows a malware name

Flag: picoCTF{Petya}

After connecting to the ssh instance i checked what the user can run as root sudo -l

So the user can run /usr/bin/vi as root
Checking gtfobins I got a shell escape command

Flag: picoCTF{uS1ng_v1m_3dit0r_ad091ce1}

After downloading the enc_flag and checking its content i see that its base64 encoded

I tried decoding it but i get another base64 encoded value

From the challenge name repetition its likely this has been encoded multiple times
So I wrote a script to decode it until it isn’t a base64 encoded value again Solve
Running it I got the flag

Flag: picoCTF{base64_n3st3d_dic0d!n8_d0wnl04d3d_4557ec3e}

Checking the link provided doesn’t show the flag

But after checking source code and using CTRL +F I got the flag

Flag: picoCTF{h34rd_und3r5700d_4ck_cba1c711}

After connecting to the ssh instance i saw a file in the current directory of the user

And its a shell script
From the challenge tag man I ran man command on the script and I got the flag

Flag: picoCTF{us3l3ss_ch4ll3ng3_3xpl0it3d_7065}

After connecting to the ssh server I get this weird shell

So it capitalize the 0th index of our input making us not able to run commands
What I did next was to find any special character value that won’t be affected from the capitaliation of the 0th index of our input

I did play around to get the value and __| worked well for me
Now we can just pass in any command we want to run
From that I got the flag

Flag: picoCTF{5p311ch3ck_15_7h3_w0r57_b741d1b1}

Just like Special Chall after loggin I got into a weird shell that doesn’t have much command

After trying some command I got that echo works

And we can access enumerate files using echo + a wildcard (*)
I checked the files in the user’s current working directory

Now we can use echo and get the content of a file to the standard output stdout
Command: echo $(</etc/passwd)
Trying that I got the flag

Flag: picoCTF{y0u_d0n7_4ppr3c1473_wh47_w3r3_d01ng_h3r3_c42168d9}

First thing first I checked the file type after downloading the file given

It’s a PNG file (image file)
On viewing it just shows the picoctf logo

Now i check its metadata

Running zsteg on it shows that there’s a ZIP file embedded in it

So i’ll use binwalk to extract it

From there I got the flag
Flag: picoCTF{Hiddinng_An_imag3_within_@n_ima9e_85e04ab8}

After downloading the file attached I checked its file type which happens to be a pcap file

Then I tried low hanging fruits by running strings on it and got the flag

Flag: picoCTF{P64P_4N4L7S1S_SU55355FUL_4d72dfcc}

After downloading the attached file I checked its file type and its happens to be a bmp file

I checked the metadata of the file but I got nothing from it

I also ran various tools to check the file but I got nothing
Then after i checked the hex dump I noticed this

That is the file header for a zip file. You can check it out here Wikipedia
So now the idea is that we can extract the values from that spot below
But first I need to get what length it is from the zip file, and I used python for it

So that means the zip file should start from offset 140
I wrote a script to extract the content of the zip file starting from index 140
Here’s the script Solve
After I run it, it dumps the zip file content

Then I used binwalk to extract it’s content

From that I got the flag
Flag: picoCTF{w0rd_d4wg_y0u_f0und_5h3113ys_m4573rp13c3_5eadc23e}

After downloading the binary I checked what file it is

And its a x64 binary which is not stripped
So i’ll run it to know what it does

Its asking for a password to unlock the file
Since I don’t currently know it I ran strings to check for low hanging fruits
And from there I got the flag

Flag: picoCTF{3lf_r3v3r5ing_succe55ful_d55531cd}

After downloading the file and checking its file type i got it to be a java compiled binary

So I used an online decompiler to decompile the .class file to a readable format
After decompiling the binary I got the flag

Flag: picoCTF{SAf3_0p3n3rr_y0u_solv3d_it_b427942b}

After downloading the file given and checking its file type it turns out to be an apk file

So here’s I did. First I unzip the content of the apk file then use dex2jar to convert the class file to a jar file which later i then use jd-gui to view the decompiled code

Flag: picoCTF{t1m3r_r3v3rs3d_succ355fully_17496}

After checking the attached file given i couldn’t get what it does
But i just connected to the netcat instance and got the flag

Flag: picoCTF{h3r0_t0_z3r0_4m1r1gh7_a7bf8a57}

This one I didn’t even download the file but I searched for what CoreWars means then I saw this resources CoreWar Payload
And when I used the payload on the remote server it worked for both Gladiator 1 & 2
Running it for Gladiator 1 works

Flag: picoCTF{1mp_1n_7h3_cr055h41r5_dba6f40d}
Running it for Gladiator 2 works

Flag: picoCTF{d3m0n_3xpung3r_106bc275}

We’re given the files to download and i downloaded the type for windows
After downloading and checking it out, it shows the unity files for a windows game

When i treid playing the game initially, i noticed the player is like limited to crossing the boundry

So after checking online for how to reverse unity file binary i got that you can edit the dll file for the game
And the particular .dll file we’re looking for should contain the program logic
It was located here

Using dnSPY, I can manipulate it
After searching around i then moveDirection variable and edited it to a constant value

Then on running the game again i got the flag after moving over the wall

Flag: picoCTF{WELCOME_TO_UNITY!!}

We’re given a remote instance to connect to which is a web server and its has a login form

Since the description says we should login as test:test1 lets try it

While i was login in i noticed the url had some base64 values before it gets redirected to home page
So i’ll login again but this time use burp to intercept the request

From here:
Right Click --> Do Intercept --> Response To Request
I got the first portion of the flag picoCTF{proxies_al

After forwarding the request i get the second portion

Flag: picoCTF{proxies_all_the_way_df44c94c}

Same as always we’re given a remote instace which is a web server
Checking it out shows that it requires giving some text

Since i don’t know the particular input to give it i checked the source code

From the js portion of the script i saw the commented part of it
// ^p.....F!?
And its a regular expression which means:
1. "^p": Matches the start of a line or string, followed by the letter "p".
2. ".....": Matches any five characters except newline.
3. "F!?": Matches the letter "F" followed by an optional exclamation mark.
So the best value to be given is picoCTF

After submitting it I got the flag

Other form of value can be used like pythonF, plololF as long as it matches the regrex
Flag: picoCTF{succ3ssfully_matchtheregex_08c310c6}

Checking the instance given shows this

Clicking on details and intercepting the request shows that the data which it’s trying to fetch is gotten using the xml format style

So since its this we can perform XXE
After checking HackTricks I made the payload to read the /etc/passwd file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE data [
<!ELEMENT data ANY>
<!ENTITY file SYSTEM "file:///etc/passwd">
]>
<data>
<ID>&file;</ID>
</data>
On submitting that I got the flag

Flag: picoCTF{XML_3xtern@l_3nt1t1ty_e5f02dbf}

Checking the web server instance given shows this login page

From the challenge title which is SQLi i’ll try using sql injection to bypass the login page
Username: ' or 1=1 --
Password: ' or 1=1 --
Trying that works

There’s another search functionality in the web app
So i’ll search for something then intercept the request with burp

Then i will save the request in a file

With this i can use sqlmap

Then I dump the whole table to see if any thing is of interest there

Flag: picoCTF{G3tting_5QL_1nJ3c7I0N_l1k3_y0u_sh0ulD_783cc6bd}

The web server source code is given
After checking it this is what i found interesting

So the key is the secret key for the jwt which is 1234
I’ll connect to the web server and login using the cred user:user

If you try to click the flag image file it won’t allow access cause the user isn’t admin

After i checked for the cookies i couldn’t find any JWT token

So i decided to login again but this time pass the request through my burp proxy

Cool so it generated a token for me and from checking jwt.io i got its algorithm to be HS256

Here’s the jwt decoded value:
{
"role": "Free",
"iss": "bookshelf",
"exp": 1679960313,
"iat": 1679355513,
"userId": 1,
"email": "user"
}
Since we know the sign key to be 1234 that means that we can generate another token
I made a script to generate the token for me
After running it i got the new token

So i then logged in again but this time intercept the request and replace the token to the newly generated own

Intercept Request --> Response To Request
Now i will replace this with the new token generated

After forwarding the request i get logged in as admin

From here we can just get the flag

Here’s my automated script to get the flag xD Solve


After downloading the file attached and checking its file type shows its an image file

It content is referring to atbash cipher and it contains the way of decoding atbash cipher
But no encrypted text is given. So now i checked if there’s any data hidden in the jpg file

Now on viewing the encrypted data it gives this

Cipher Text: krxlXGU{zgyzhs_xizxp_1u84w779}
We can use that table in the image already given or an online tool Dcodefr to decode it

Flag: picoCTF{atbash_crack_1f84d779}

After downloading the file attached i checked its file type and its a PEM certificate request file

So if we view it what we will see is just the certificate file

Next thing i did was to search how to decode .csr file with openssl
And i got this Resource
I put it in a bash script

And after running it i got the flag

Flag: picoCTF{read_mycert_a7163be8}

We’re given the encrypted file

So i used DcodeFR to identify the cipher type and used it also for decoding it

Its likely a rot cipher so i used Decoder to decode it

Flag: picoCTF{r0tat1on_d3crypt3d_25d7c61b}

We’re given the remote file being used
Here’s its content

And this is what’s happening
1. It creates 16 random values which are either ascii or digits
2. After that it generates random prime numbers with 128 bits in two variables
3. It then multiplies those two variable which is stored in a new variable
4. 65537 is stored in variable sloth
5. The multiplicative inverse of sloth and (gluttony - 1) * (greed - 1) is done and stored in variable envy
6. It converts the value of pride to a long integer then does the modular exponential of pride where the public exponent is sloth and the modulus is lust
Overall this is a process of encrypting a message using RSA
So if we run the prorgam it will generate the private key (d) and its ciphertext(c)
Then it asks for our input and check if its equal to the value stored in pride. If the check is meet we get the flag else it prints out Hubris!
But now that we know both the private key and the ciphertext we can calculate:
1. kphi from ed-1
2. Then go to factordb and take every prime factor which will be put in a list
3. From there we can use any for loop with product from itertools to calculate the multplication of some factors and check the result of this multiplication which will be called n
4. Then we check if isPrime(n+1) and if (n+1).bit_lenght() = 128. If the check is meet we can save (n+1) in a list
5. With this we will get a list of all possible primes that could construct N
6. We can then make two for loops where i and j are the iterate with the list and calcualte N=i*j; if long_to_bytes(pow(c,d,N)).isprintable(): print(ong_to_bytes(pow(c,d,N)))
Thats the way I solve it
So I wrote a script for it Solve
Running it gives the flag
➜ sage solve.py
[-] Opening connection to saturn.picoctf.net on port 51615: Connected
anger = 10874849782922899596926546059091484448219162477034704964483223843381834331753
envy = 6079395884215855559684171323910149420899924737311416787335412267518949259009
vainglory?
picoCTF{7h053_51n5_4r3_n0_m0r3_3858bd66}
➜ sra
Flag: picoCTF{7h053_51n5_4r3_n0_m0r3_3858bd66}

I checked what file type it is and the protections enabled on the binary

Its a x86 binary which is dynamically linked and not stripped, the protections enabled on the binary are Canary and NX (No Execute)
Next i’ll run it to get an idea of what it does

Looks like some sort of game
Using ghidra i decompiled the binary
Here’s the main function

This is the part am interested about
} while (local_aac != 0x1d);
} while (local_aa8 != 0x59);
puts("You win!");
if (local_aa4 != '\0') {
puts("flage");
win();
fflush(stdout);
If local_aac == 0x1d and local_aa8 == 0x59 it will print out you win then another check is done that checks if the value of local_aa4 is not equal to a null terminator it will call the win() function which will give the flag

To trigger this condition we must firstly win the game and the local variable local_aa4 must be non-zero to get the flag.
So the way to move the player of the game is by using w,a,s,d and other good functions are p,l

And here’s what each does:
1. w --> moves the player up
2. a --> moves the player left
3. s --> moves the player right
4. d --> moves the player down
5. l --> change the player structure
6. p --> auto solve the game
And what I noticed while i tried it is that when the player goes out of bound you get a seg fault

The reason is cause the program tried accessing a memory address that isn’t valid

Looking at the stack layout

| local_aac | [$EBP - 0xAAC] player_x_pos (or row in map)
| local_aa8 | [$EBP - 0xAA8] player_x_pos (or column in map)
| local_aa4 | [$EBP - 0xAA4] char flag_win_condition (!= 0)
| local_aa0 | [$EBP - 0xAA0] char[2700] map_buf
The offset between the player_x_pos and local_aa4 is 0x4
➜ game01 python3
Python 3.11.1 (main, Dec 31 2022, 10:23:59) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> hex(int(0xaa8)-int(0xaa4))
'0x4'
>>> 0x4
4
>>>
This also tells us our initial starting position is always { X, Y } = { 4, 4 }
Now the idea is that if we can reach that particular position 0x59 = 89 and also go out of bound we can overwrite the value of local_aa4 by adding extra 4 bytes since thats the required offset needed to overwrite local_aa4
The function init_player() takes the address of local_aac and writes three words, array indexing flowing into subsequent parameters of main()
local_aac[0] = 4 = player_y_pos (or row in map)
local_aac[1] = 4 ---> local_aa8 = player_x_pos (or column in map)
local_aac[2] = 0 ---> local_aa4 = flag_win_condition (!= 0)
init_map() function confirms the purpose and ordering of the player position coordinates, as detailed above. It also exposes the exit position which is the game win condition, that is the player must reach { 29, 89 } = { 0x1d, 0x59
And the decompilation of the move_player function is
if (param_2 == 'w') {
*param_1 = *param_1 + -1;
}
else if (param_2 == 's') {
*param_1 = *param_1 + 1;
}
else if (param_2 == 'a') {
param_1[1] = param_1[1] + -1;
}
else if (param_2 == 'd') {
param_1[1] = param_1[1] + 1;
}
*(undefined *)(*param_1 * 0x5a + param_3 + param_1[1]) = player_tile;
But if we substitute with our variable names and making the code a little easier to read :
*map_buf[(player_y_pos * 0x5a) + player_x_pos)] = player_title
What we can notice there is that:
1. There is no bounds checking on position increment/decrement operations
2. These new player position values form the index of an array dereference
This is the vulnerable part of the code because we control the player position values and hence can control the dereferenced array entry (and hence address) and the character that gets written there, forming a write-what-where primitive.
So our aim here will be to underflow the map_buf array and write a non-zero value to the local_aa4 our ` if (local_aa4 != ‘\0’) {` condition , which was initialised to zero by init_player()
This means an effective position of { 0, -4 } will write the player_title character to the LSB of local_aa4
Now to solve this we need to move player position from { 4, 4 } to { 0, -4 }, this underflows the map_buf[] and writes the player’s position character over local_aa4 (satisfying the non-zero flag win condition)
Payload: aaaawwwwaaaap
So what that does is to:
1. Move left 4 times
2. Go up 4 times
3. Go out of bound with 4 bytes
4. Auto win the game
Doing that gets me the flag locally

So I did the same remotely

Here’s the auto solve script Solve
Running it gives the flag

Flag: picoCTF{gamer_m0d3_enabled_3aa88304}

The source code is given

Here’s what it does:
1. Creates a function called addIntOvf and it requires 3 parameters which are result, a & b
2. What the function does is to sum up int a and int b and store them in result
3. It then checks if the value of a and b is greater than 0 and the value of result is less than 0
4. It then returns error
5. Also an if check is done that checks if the value of a and b is less than 0 and the result is greater than 0
6. If its that case it returns error
7. Else it exits
For the main function here’s what it does:
1. It prints out n1 > n1 + n2 or n2 > n1 + n2
2. Then it prints What two positive numbers can make this possible:
3. It receives our input which are two numbers stored in &num1 and &num2
4. A variable sum is created which takes the sum of num1 and num2
5. Then it calls the addIntOvf function which takes in the sum, &num1, &num2 as parameters and check if it returns true
6. If that returns true it prints out No overflow
7. Else if addIntOvf returns false it prints out You have an integer overflow
8. After that it then checks if the value of &num1 and &num2 are greater than 0 (non negative number)
9. If the check is meet it prints out the flag
Looking at the code what the program expects is two positive numbers that when sumed up gives a negative number
Is that possible?
Yes!!! And its called an integer overflow
So basically performing integer overflow means going above the maximum value of the signed integer, and the result usually becomes a negative number
Here’s the resource Resource
This are two integers that when added result in an integer overflow
Num1: 2147483647
Num2: 1
Giving that as the num to the binary gives an integer overflow and then the flag (locally)

Trying that on the remote server works also

Here’s the solve script Solve
Running it gives the flag

Flag: picoCTF{Tw0_Sum_Integer_Bu773R_0v3rfl0w_e06700c0}

After downloading the binary i checked its file type and the protection enabled

Cool the only protection enabled is No-Execute
Running the binary shows the same output as the previous one

So i decompiled it using ghidra and now here’s the difference between the it and the first binary

After the user wins it doesn’t print any flag or calls the win function

Since its the same function as the previous binary, i won’t share ss of the whole decompiled code
Visualising the stack frame of main() from the disassembly to show placement and relative offsets of local variables and other elemts on the stack. I’ve augmented the variables with their purpose obtained from analysis of functions (main(), init_player() and move_player())
**************************************************************
* FUNCTION *
**************************************************************
undefined main(undefined1 param_1)
undefined AL:1 <RETURN>
undefined1 Stack[0x4]:1 param_1 XREF[1]: 08049674(*)
undefined4 Stack[0x0]:4 local_res0 XREF[1]: 0804967b(R)
undefined1 Stack[-0x10]:1 local_10 XREF[1]: 08049753(*)
undefined1 Stack[-0x11]:1 local_11 XREF[2]: 080496eb(W),
080496ee(R)
undefined1 Stack[-0xa9d local_a9d XREF[4]: 080496aa(*),
080496c3(*),
080496f5(*),
08049716(*)
undefined4 Stack[-0xaa4 local_aa4 XREF[1]: 08049730(R)
undefined4 Stack[-0xaa8 local_aa8 XREF[6]: 08049694(*),
080496a3(*),
080496bc(*),
080496fd(*),
0804970f(*),
08049725(R)
main XREF[5]: Entry Point(*),
_start:08049110(*),
_start:08049116(*), 0804a0e8,
0804a2a8(*)
08049674 8d 4c 24 04 LEA ECX=>param_1,[ESP + 0x4]
move_player(player_y_pos, input_ch, map_buf) contains the same exploitable line of code that forming a write-what-where primative. We control the player X and Y position and hence the address to write to, aswell as the byte to write through the ability to change the player_title
So the vulnerable part of the code is
map_buf[(player_y_pos * 0x5a) + player_x_pos)] = player_title
This time we must overwrite a return address on the stack with the addres of the win() function to gives the flag instead of overwriting a local variable of main().
The behaviour of move_player() is slightly different in babygame02, in that before moving the player it writes to the previous player position (before move), clearing it with ‘.’ (0x2e) character before updating and writing the new player position. This means cycling this exploit to write multiple bytes is not possible as it corrupts the previous byte written with the clear. Therefore we need to find a location where only one write operation is required to divert the flow of execution to the win() function address.
After playing with the binary i noticed is that at address 0x080495b6. The value at $esp will be the return pointer

What we can see there is
*EIP 0x80495b6 → <solve_round+47> add esp, 0x10 ← $esp
It's showing that the player_move was called from the solve_round function. So our input 'p', we won't need to use the p command for game02
Now here’s what happen when i move around the map and check the stack memory address
After setting a new breakpoint at 0x8049548 = <move_player+212>: ret

Now that @ is the player, and we’re moving it around in stack memory
And this is what happens when we go out of bound, the program will crash and its so cause the instruction pointer will try to access an invalid memory address
Now when we move round the memory it’s pointers on the stack that we’re modifying. Now the questions is… can we modify something useful to control the execution of the program so that we can get it to print the flag?
The goal is to make the eip call the win() function
But the problem is even tho we can overwrite the @ structure it only just overwrites the last byte of an address of the stack
We need something similar to the win() function address and after setting a breakpoint at *solve_round+47

Then checking the stack, i got an address of the stack that looks very similar to the win() function

We can see the similarity between the stack address 0x08049709 and the win function address 0x0804975d
Only one byte that makes it not the same as the win function address
Now next thing is that since while we move round the map and out of bound, the player character is also moving round the stack therefore overwriting bytes on the stack which makes the program crashes, the idea is that that particular memory on the stack can be overwritten to the win function address.
And for this to occur the character needed to overwrite it is ] which will later turn to 5d in hex
➜ game02 python
Python 3.11.1 (main, Dec 31 2022, 10:23:59) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> a=']'
>>> b=ord(a)
>>> hex(b)
'0x5d'
>>>
And we know that the l function changes the player character to anything specified. So that means we can change our player character to ]
So basically, when we jump up and down, we jump in the memory. Cause 2d arrays in memory become 1d with offsets and since we know the width of the map to be 89, we know that we jump 89 bytes every time we move up and down.
We can control where this 0x5d ends up as this ] which is now the player so try and find the position on the map that is 89 bytes offset from that address that is close to the win function which is the stack address we got earlier.
So i just made a script to move round the map but out of bound i.e w
Here’s my local fuzz script Fuzz_Local
Running it locally gets me the offset

Then i can get local flag

python2 -c "print 'l]' + 'wwww' + 'd'*47 + 'wp'" | ./game
But trying it remotely doesn’t work

So its likely the stack offset i think
Then a made a brute script and auto solve script for the remote own
Here’s the Exploit
Running it works and i got the flag

Flag: picoCTF{gamer_jump1ng_4r0unD_498dff8d}

After i connected to the ssh remote instance i saw this file called .server.py

Since its own by root and the aim of this chall is to get root so i decided to take a look at it

What the script does is to attempt to gather information by pinging picoctf.org
But we don’t have any write access over this file

So now what we can do is a python library hijack
Since those libraries i.e base64, os, socket are imported in the file we can attempted to hijack and add malicious content to those library files so that when its is being called when the .server.py is executed our malicious command will also be executed
But first we need to have write access over any of the library files
And base64.py seems ok to be abused

So now we can add in any thing we want inside the base64.py file then after we run .server.py it will be executed
I added a command to make /bin/bash an suid binary

Now checking sudo -l shows we can run the .server.py as root

And that is ok cause we are trying to change the permission of a binary to suid
From here we can run the script

We can see now that /bin/bash permission is now that of an suid binary
Root should be easy from here 👻

Flag: picoCTF{pYth0nn_libraryH!j@CK!n9_0083cb0b}

After connecting to the ssh instance i got this two files

Looking at the source code and the binary perm, it shows the binary is a suid binary

And the source code for the txtreader binary is:
1. A C++ program
2. The program checks if the user has provided exactly one command line argument (the filename to be read). If not, it displays a usage message and exits with an error code of 1
3. The program opens the file using an input file stream (std::ifstream).
4. The program uses the stat() system call to retrieve the file's status information, which includes the owner's user ID. If stat() returns an error (-1), the program displays an error message and exits with an error code of 1.
5. The program compares the owner's user ID to the ID of the current user (obtained using getuid()). If they don't match, the program displays an error message and exits with an error code of 1.
6. The program reads the contents of the file line by line using std::getline() and outputs each line to the console using std::cout.
7. If the file cannot be opened, the program displays an error message and exits with an error code of 1.
8. If everything goes well, the program exits with a success code of 0.
Overall, this program reads a file and outputs its contents to the console, but only if the user has permission to read the file (i.e., if they are the owner)
At this point what we would love to read is the flag.txt file

But the problem now is the file is only own by root
So since the program reads files owned by the user, we won’t be able to read the flag.txt
Thats what its normally supposed to do but here’s the vulnerability
From the task description it says toc-tou which means Race Condition

After reading some of the articles i found a way to exploit this
And its using symlink
Here’s the exploitation phase:

touch fake_flag
while true; do ln -sf flag.txt link; ln -sf fake_flag link; done &
while true; do ./txtreader link 2>/dev/null; done > val &
cat val
From that we can get the flag
Flag: picoCTF{ToctoU_!s_3a5y_5748402c}

After i connected to the ssh instance i saw a binary there and ran it
But i got this

So it reqires the SECRET_DIR environment to be set
It can be easily set like this

So from the output we can tell that the binary does directory listing on the path specified
Now what we can try here is command injection in the environment variable
Doing that works

Flag: picoCTF{Power_t0_man!pul4t3_3nv_fc3ff2c9}
Thats all 👻
Here’s the link to the writeup Link