无字母数字命令执行黑魔法——shell脚本变量
2022-12-9 15:27:0 Author: xz.aliyun.com(查看原文) 阅读量:57 收藏

前言

无字母数字webshell算是一个很老生常谈的话题了,但由于利用条件比较苛刻,一般只会在CTF题目中出现,作为一种有趣的绕过的思路,p牛关于这个问题也写过两篇非常出色的博客,通过p牛的博客我也学到了许多有趣的bypass技巧。最近翻看其他大佬博客的时候,对于这个问题我又发现了一个很有趣的小tips,那就是通过linux中的某些特殊变量执行命令,或许这种技巧能在真实的环境或者CTF赛题中出现。

前置知识

首先介绍一下shell脚本中$的多种用法(参考):

变量名 含义
$0 脚本本身的名字
$1 脚本后所输入的第一串字符
$2 传递给该shell脚本的第二个参数
$* 脚本后所输入的所有字符’westos’ ‘linux’ ‘lyq’
[email protected] 脚本后所输入的所有字符’westos’ ‘linux’ ‘lyq’
$_ 表示上一个命令的最后一个参数
$# #脚本后所输入的字符串个数
$$ 脚本运行的当前进程ID号
$! 表示最后执行的后台命令的PID
$? 显示最后命令的退出状态,0表示没有错误,其他表示由错误

从博客中引发的思考

我看大佬的博客的时候他注意到: Linux变量$_,它存储着上次程序传入的参数,比如执行echo can you get the file of tmp命令后,再执行echo $_,发现结果是tmp

因此给出一个特殊的题目条件,比如:

<?php
if(isset($_GET['code'])){
    $code = $_GET['code'];
    if(strlen($code)>10){
        die("Long.");
    }
    if(preg_match("/[A-Za-z0-9]+/",$code)){
        die("NO.");
    }
    eval("system(\"echo can you get the file of tmp;".$code."\")");
}else{
    highlight_file(__FILE__);
}

此时我们使用payload

?code=. /\$_/*

然后配合临时文件上传我们就可以执行命令,甚至get shell。

但大佬在文章的末尾提到:

于是当天我就想到了个出CTF题的有趣点子,其实对于以get flag为目的的ctf题目而言,对于payload的长度可以大大缩减,我们来本地做个测试:

我们先到根目录下放个flag:


然后切换到桌面用那个黑魔法获得flag,我当时本地测试时想到的payload可以只用五个字符:

?code=. /$_


原理很简单,就是在linux里可以用点号+空格+文件名执行一个可执行文件,等效于source可执行文件,然后我们前面echo了一次flag,flag作为了最后一个参数,因此可以用$_代替这个flag,但又因为我们这个flag不是可执行文件,因此linux就会报错,然后打印并输出这个文件里的内容,类似于用date -f越权读文件一样。

然后我就构思一个题目,分享在了学校的信安协会群里:

<?php 
if(isset($_GET['command'])){
    $command = $_GET['command'];
    if(strlen($command)>5){
        die("Too Long!");
    }
    if(preg_match("/[A-Za-z0-9]+/",$command)){
        die("No letters or numbers!");
    }
    eval(system("echo you are not able to get flag;$command 2>&1"));
}else{
    highlight_file(__FILE__);
} 
?>

因为要靠报错输出flag嘛,所以必须加上2>&1,但这样也导致这个题目的破局点比较明显了,我也没想到什么更好的方法,这个代码也只是一个雏形。

然后预期解当然就是?command=. /$_

放在群里之后没过多久就有人做出来了,而且还只用了三个字符,猜猜是什么?

没错,是直接?command=/$_

这种做法大大的出乎了我的意料,因为这样做我出题的时候本地是失败了的:

后来我一下就想到了,肯定是权限的问题。我当时在宝塔后台随便就创建了一个flag文本,到后台一看,果然不但有阅读权限更有执行权限:

把执行权限关了就只能用我那个预期解了:


原理也很简单,就是直接用/$_代替/flag了,相当于直接用文件名执行脚本,然后打印报错输出,就不用再加个点号了。

出题后的感悟

不过这次有趣的题目分享也激发了更多我对这种无字母数字ctf题目的思考,在限制和过滤足够多的情况下,只是用$当作给变量命名的工具其实是大大局限了它的作用,因为shell脚本中很多变量名都是有特殊含义的,如果配合一定条件,它能发挥十分神奇的作用,比如我上面出的那个题,就可以在限制足够多的条件用三个字符就拿到flag,而其他的变量同样能发挥神奇的作用:

我们知道$#可以表示#脚本后所输入的字符串个数,在默认情况下就直接表示零了:

而我们可以通过自增运算表示不同的数字:

这也相当于一种绕过限制表达数字的方式了。

同样的,$?可以显示最后命令的退出状态,我们也可以用它来构造出数字来:

其他的变量也有很神奇的作用,不过利用条件其实都挺苛刻的,但或许能通过不同变量打一套组合拳实现get shell,比如像大佬文章里的那样配合临时文件甚至可以直接get shell。但这种利用条件终究有点太难了,感觉只有可能在CTF题目作为一个有趣的考点,很难在实战中发挥作用,我在这里就算抛砖引玉了,希望大家能有更多有趣的见解和想法。

参考:

关于p神的无字母数字webshell之提高篇的思考

shell脚本中$的多种用法($* 、 [email protected] 、$_ 、$# 、$$ 、$! 、 $? )


文章来源: https://xz.aliyun.com/t/11934
如有侵权请联系:admin#unsafe.sh