// HackTheBox  ·  writeup

Knock Knock

HackTheBox Medium WiresharktsharkstringsPythonGitHub
DFIR

Scenario

You are thrusted into a digital crisis at Forela - a crucial Dev server has fallen victim to a cyber attack. This incident unfolds at a critical time for the company as it is expanding its business in Pakistan, under the leadership of senior dev Abdullah. Despite assurances of server hardening, the server was mistakenly exposed to the internet, creating a doorway for the threat actor. The task involves swift analysis & reporting to prevent catastrophic damage to the company’s operations. Equipped with a packet capture from the time of the incident, players must sift through a sea of data, with a 1-2 days margin of uncertainty. This cybersecurity whodunit will put your forensic skills to the test, seeking answers to how the attacker breached their defenses in the first place.

Warning: This Sherlock will require an element of OSINT to complete fully.

Executive Summary

An external host scanned the exposed Forela dev server, found FTP among a handful of open services, and sprayed one predictable password across several usernames until an account logged in. That account’s FTP home held a hidden backup file with a port knocking configuration and credentials. The attacker replayed the knock sequence, which opened a hidden second FTP service, and logged in there with a developer’s credentials. The service exposed a database dump with cloud credentials, planning documents and a way out of the chroot to read system files, and a reminder note pointed to a public GitHub repository whose history leaked the SSH password. The attacker then logged in over SSH and pulled a ransomware archive from a separate server over HTTP.

Artifacts

Timestamp note: Time answers use DD/MM/YYYY HH:mm:ss in UTC. Force TZ=UTC on tshark before reading frame.time.

Note: The hidden FTP service is not dissected as FTP by default. Use -d tcp.port==<port>,ftp (Wireshark: Decode As) to see its commands.

Discovery

Q1 — Which ports did the attacker find open during their enumeration phase?

Triage the capture by conversation first. One external host sends bare SYNs across a wide port range, which is the scanner. Open ports are the ones that answer with a SYN-ACK, so filter on tcp.flags.syn==1 && tcp.flags.ack==1 towards the scanner and list the distinct source ports.

tshark -r Capture.pcap -q -z conv,tcp
tshark -r Capture.pcap -Y 'ip.dst==$SCANNER && tcp.flags.syn==1 && tcp.flags.ack==1' -T fields -e tcp.srcport | sort -nu

Tip: Count only SYN-ACKs. Closed ports answer with RST and pad the list.

Click flag to reveal 21,22,3306,6379,8086
Q2 — What was the UTC time when the attacker started their attack against the server?

The first packet from the scanner marks the start. Force UTC on frame.time and reformat to the day-first mask.

TZ=UTC tshark -r Capture.pcap -Y 'ip.src==$SCANNER' -T fields -e frame.time -c 1
Click to reveal answer 21/03/2023 10:42:23

Initial Access

Q5 — What is the Malicious IP address utilized by the attacker for initial access?

The scanner from Q1 is also the host that authenticates to the FTP service. One external address owns both the scan and the FTP session.

tshark -r Capture.pcap -Y 'ftp' -T fields -e ip.src -e ip.dst -e ftp.request.command -e ftp.request.arg
Click flag to reveal 3.109.209.43
Q3 — What's the MITRE Technique ID of the technique attacker used to get initial access?

Port 21 is the way in. The attacker tries a short list of seasonal passwords against three different usernames, so the pattern is one password pattern across many accounts, not many passwords against one account. That is password spraying.

tshark -r Capture.pcap -Y 'ftp.request.command=="USER" || ftp.request.command=="PASS"' -T fields -e tcp.stream -e ftp.request.command -e ftp.request.arg

Gotcha: T1110.001 (Password Guessing) is the obvious first pick, and HTB rejected it. The traffic is a spray across three users, which is the .003 sub-technique.

Click to reveal answer T1110.003
Q4 — What are valid set of credentials used to get initial foothold?

Most sprayed pairs fail with 530. Credentials only count if the server answers 230 in the same TCP stream, so list the streams that got a 230 and read the PASS command from each.

tshark -r Capture.pcap -Y 'ftp.response.code==230' -T fields -e tcp.stream
tshark -r Capture.pcap -Y 'ftp.request.command=="PASS" && tcp.stream==<stream>' -T fields -e ftp.request.arg

Gotcha: Another user appears in the spray with the same password but never reaches a 230, and submitting that pair was wrong. Confirm the success code in the stream rather than taking the first username.

Click to reveal answer tony.shephard:Summer2023!

Collection

Q6 — What is name of the file which contained some config data and credentials?

After login the attacker lists the home directory and issues RETR for a hidden file. The data channel carries a port knocking configuration plus credentials used for the rest of the intrusion.

tshark -r Capture.pcap -Y 'ftp.request.command=="RETR"' -T fields -e tcp.stream -e ftp.request.arg
tshark -r Capture.pcap -q --export-objects ftp-data,work/ftpobj
Click to reveal answer .backup

Defense Evasion

Q7 — Which port was the critical service running?

The exported hidden file holds a knockd-style config. It names the protected service port that stays closed until the knock sequence completes.

strings work/ftpobj/.backup
Click flag to reveal 24456
Q8 — Whats the name of technique used to get to that critical service?

The service only opens after connection attempts hit a fixed sequence of closed ports in order. That is port knocking, MITRE T1205.001 (Traffic Signaling: Port Knocking).

Click to reveal answer Port Knocking
Q9 — Which ports were required to interact with to reach the critical service?

Take the knock sequence from the Q7 config. In the capture, the attacker’s bare SYNs hit those three ports in order, and the service port answers afterwards.

tshark -r Capture.pcap -Y 'ip.src==$ATTACKER && tcp.flags.syn==1 && tcp.flags.ack==0 && tcp.dstport in {<knock ports from .backup>}' -T fields -e frame.time -e tcp.dstport
Click flag to reveal 29999,45087,50234
Q10 — Whats the UTC time when interaction with previous question ports ended?

Same filter as Q9 with UTC forced. The timestamp of the last knock SYN is when the interaction ended.

TZ=UTC tshark -r Capture.pcap -Y 'ip.src==$ATTACKER && tcp.flags.syn==1 && tcp.flags.ack==0 && tcp.dstport in {<knock ports from .backup>}' -T fields -e frame.time
Click to reveal answer 21/03/2023 10:58:50

Lateral Movement

Q11 — What are set of valid credentials for the critical service?

The hidden service is a second FTP server. Force the FTP dissector on its port, then read the USER and PASS commands from the stream that ends in 230.

tshark -r Capture.pcap -d tcp.port==24456,ftp -Y 'tcp.port==24456 && (ftp.request.command=="USER" || ftp.request.command=="PASS")' -T fields -e tcp.stream -e ftp.request.command -e ftp.request.arg

Tip: Without the decode-as, the traffic is raw TCP and no FTP commands match the filter.

Click flag to reveal abdullah.yasin:XhlhGame_90HJLDASxfd&hoooad
Q12 — At what UTC Time did the attacker get access to the critical server?

The time of the 230 login response from the hidden service, after the knock completed.

TZ=UTC tshark -r Capture.pcap -d tcp.port==24456,ftp -Y 'ftp.response.code==230 && tcp.srcport==24456' -T fields -e frame.time
Click to reveal answer 21/03/2023 11:00:01

Collection

Q13 — Whats the AWS AccountID and Password for the developer "Abdullah"?

Export the FTP data objects from the second service and read them. A hidden SQL dump holds the developer’s AWS account ID and password.

tshark -r Capture.pcap -d tcp.port==24456,ftp -q --export-objects ftp-data,work/ftpobj2
strings work/ftpobj2/.archived.sql | grep -i abdullah
Click flag to reveal 391629733297:yiobkod0986Y[adij@IKBDS
Q14 — Whats the deadline for hiring developers for forela?

An exported Tasks archive contains Done.docx, which embeds an Excel sheet. The deadline is stored as an Excel serial number in sheet1.xml (45168), which converts to a calendar date counting from 1899-12-30.

python3 -I -c "import zipfile,sys;print(zipfile.ZipFile(sys.argv[1]).namelist())" work/ftpobj2/Tasks
python3 -I -c "import datetime;print(datetime.date(1899,12,30)+datetime.timedelta(days=45168))"
Click to reveal answer 30/08/2023
Q15 — When did CEO of forela was scheduled to arrive in pakistan?

reminder.txt is among the exported objects and states the CEO’s travel date.

cat work/ftpobj2/reminder.txt
Click flag to reveal 08/03/2023

Discovery

Q16 — The attacker was able to perform directory traversel and escape the chroot jail.This caused attacker to roam around the filesystem just like a normal user would. Whats the username of an account other than root having /bin/bash set as default shell?

Once out of the chroot, the attacker retrieves /etc/passwd over FTP. Filter the exported copy for /bin/bash shells; root is expected and one other account matches.

grep '/bin/bash' work/ftpobj2/passwd
Click to reveal answer cyberjunkie
Q17 — Whats the full path of the file which lead to ssh access of the server by attacker?

The attacker also retrieves a hidden reminder file from outside the chroot. Its contents are a note pointing at a public GitHub repository, which is where the SSH password comes from. Read the CWD/RETR arguments for the full path.

tshark -r Capture.pcap -d tcp.port==24456,ftp -Y 'ftp.request.command=="RETR" || ftp.request.command=="CWD"' -T fields -e frame.time -e ftp.request.arg
cat work/ftpobj2/.reminder
Click flag to reveal /opt/reminders/.reminder

Credential Access

Q18 — Whats the SSH password which attacker used to access the server and get full access?

SSH is encrypted, so the password never appears in the capture. The note from Q17 points at a public GitHub source and the scenario warns about OSINT. Searching users for names seen in the capture goes nowhere, but a plain repository search for the company name finds a Forela repository created the day before the intrusion. The current tree is clean. The history is not: the initial commit contains a config file with an SSH password entry for the account found in Q16, and a later commit removes it. Read the patch of the first commit and take the value from the + line. This answer was found on public GitHub, not in the pcap.

curl -s 'https://api.github.com/search/repositories?q=forela'
curl -s https://api.github.com/repos/<owner>/<repo>/commits
curl -s -H 'Accept: application/vnd.github.patch' https://api.github.com/repos/<owner>/<repo>/commits/<first-commit>

Gotcha: The file looks sanitised in the latest commit. The secret survives only in the first commit’s diff, so walk the full history of a leaked repo.

Click flag to reveal YHUIhnollouhdnoamjndlyvbl398782bapd

Impact

Q19 — Whats the full url from where attacker downloaded ransomware?

After SSH access, the capture shows an HTTP request from the victim to a second external host. http.request.full_uri gives the URL, a zip under a campaign path named after the target.

tshark -r Capture.pcap -Y 'http.request' -T fields -e ip.dst -e http.request.full_uri
Click flag to reveal http://13.233.179.35/PKCampaign/Targets/Forela/Ransomware2_Server.zip
Q20 — Whats the tool/util name and version which attacker used to download ransomware?

The User-Agent of the same request identifies the client.

tshark -r Capture.pcap -Y 'http.request' -T fields -e http.user_agent
Click to reveal answer Wget/1.21.2
Q21 — Whats the ransomware name?

--export-objects http missed the large transfer, so follow the TCP stream as raw bytes, strip the HTTP headers and carve the zip. The archive listing names the ransomware.

tshark -r Capture.pcap -q -z follow,tcp,raw,78238 > work/ransom_raw.bin
unzip -l work/Ransomware2_Server.zip

Tip: Carve with follow,tcp,raw when --export-objects http returns nothing for a large download.

Click flag to reveal GonnaCry

Attack Summary

PhaseAction
DiscoveryPort scan finds 21, 22, 3306, 6379 and 8086 open
Initial AccessFTP password spray across three users; tony.shephard logs in
CollectionHidden .backup file retrieved from FTP
Defense EvasionKnock sequence sent to three closed ports, opening the hidden service on 24456
Lateral MovementLogin to the second FTP service as abdullah.yasin
CollectionDatabase dump, task document and reminder file retrieved
DiscoveryChroot escape and /etc/passwd read; cyberjunkie identified
Credential Access.reminder points to a leaked repository; the SSH password sits in its first commit
ImpactRansomware archive downloaded over HTTP from a second server

IOCs

TypeValue
IP (scanner, FTP, SSH)3.109.209[.]43
IP (ransomware host)13.233.179[.]35
URLhxxp[://]13[.]233[.]179[.]35/PKCampaign/Targets/Forela/Ransomware2_Server.zip
Account (FTP)tony.shephard
Account (hidden FTP)abdullah.yasin
File.backup, .archived.sql, /opt/reminders/.reminder
Port knock sequence29999, 45087, 50234 (service on 24456)
User-AgentWget/1.21.2
MalwareGonnaCry
Repositorygithub[.]com/forela-finance/forela-dev (commit 182da42)

MITRE ATT&CK

TechniqueIDDescription
Network Service DiscoveryT1046SYN scan of the server
Brute Force: Password SprayingT1110.003Seasonal password sprayed across FTP users
Valid AccountsT1078Logins to both FTP services and SSH
Traffic Signaling: Port KnockingT1205.001Knock sequence opened the hidden FTP service
File and Directory DiscoveryT1083Chroot escape and filesystem browsing
Data from Local SystemT1005Backup, SQL dump, documents and /etc/passwd retrieved
Ingress Tool TransferT1105Ransomware archive fetched with wget
Data Encrypted for ImpactT1486GonnaCry ransomware archive downloaded to the host

Defender Takeaways

Plain FTP exposed to the internet with seasonal passwords is an open door. The spray used one predictable pattern across three accounts. Drop FTP for SFTP, ban password patterns tied to the year, and alert on a burst of 530 responses from one source followed by a 230.

A backup file inside the FTP root leaked the defence. .backup held the knock sequence and credentials, so the one control meant to hide the critical service was given away by the first foothold. Keep configs and secrets out of any directory reachable by a low-privilege service account.

Port knocking is obscurity, not authentication. The service on 24456 was still plain FTP with a developer login. Anyone who reads the sequence or watches the traffic can replay it. Put the service behind a VPN or key-based access instead.

A chroot that can be traversed gives no isolation. The attacker read /etc/passwd and /opt/reminders, and the note there led to the SSH password. Fix the FTP server’s jail and keep credentials and hints out of readable reminder files.

Plaintext credentials in shared dumps and public repositories travel far. A database dump held cloud account credentials, and the SSH password sat in the first commit of a public repository, still readable after a later commit removed it. Rotate anything found in dumps, scan public repos and their full history for company secrets, and treat a removed secret as burned.

Conclusion

The attacker went from a scan to a ransomware download on the server using a weak FTP password, a leaked config, and credentials that had already been exposed on GitHub. Port knocking hid the critical service but guarded nothing once the sequence was read from the first stolen file.