// HackTheBox  ·  writeup

PhishNet

HackTheBox Very Easy Pythonemail parserhashlib
SOC

Scenario

This Sherlock case involves a phishing attack where an attacker impersonates a known vendor to deceive an accounting team. The email, designed to appear legitimate, includes an urgent payment request and a ZIP attachment containing malware. By analyzing the email’s headers and content, investigators can identify the phishing tactics used and the potential risks involved. This case underscores the need for careful scrutiny of unexpected emails and attachments to prevent security breaches.

Executive Summary

An attacker posing as a known vendor emailed an accounting team with an urgent payment request. The message came from a domain the attacker controlled, so SPF passed, while the Reply-To quietly pointed at a different mailbox on the same domain. The body linked to a lookalike subdomain and carried a ZIP “invoice” whose only member was a batch script wearing a fake PDF extension. The archive was never extracted or run, so the script’s behaviour is not established, but the delivery chain is a standard attachment phish built to get a finance user to double-click.

Artifacts

Timestamp note: No question asks for a time, so none are tabulated here.

Initial Access

Q1 — What is the originating IP address of the sender?

Dump the headers and look for the sender’s own address rather than a relay. The X-Originating-IP header holds it, and it differs from the address on the Received chain, which is the next question.

python3 -I -c "import email,sys; from email import policy; m=email.message_from_file(open(sys.argv[1]),policy=policy.default); print(m['X-Originating-IP'])" evidence/email.eml
Click flag to reveal 45.67.89.10
Q2 — Which mail server relayed this email before reaching the victim?

Read the Received headers top to bottom. The topmost one is the final hop, mail.business-finance.com handing the message to mail.target.com, and the IP on that hop is the relaying server.

python3 -I -c "import email,sys; from email import policy; m=email.message_from_file(open(sys.argv[1]),policy=policy.default); print('\n'.join(m.get_all('Received')))" evidence/email.eml

Gotcha: Two IPs sit in the headers. X-Originating-IP is the sender (Q1); the relay IP comes from the Received line.

Click to reveal answer 203.0.113.25
Q3 — What is the sender's email address?

From and Return-Path agree, so there is no envelope/header split to untangle.

python3 -I -c "import email,sys; from email import policy; m=email.message_from_file(open(sys.argv[1]),policy=policy.default); print(m['From'], m['Return-Path'])" evidence/email.eml
Click flag to reveal [email protected]
Q4 — What is the 'Reply-To' email address specified in the email?

Compare Reply-To against From. They are different mailboxes on the same domain, so a reply never reaches the address the message appears to come from. That mismatch is the tell.

python3 -I -c "import email,sys; from email import policy; m=email.message_from_file(open(sys.argv[1]),policy=policy.default); print(m['Reply-To'])" evidence/email.eml
Click to reveal answer [email protected]
Q5 — What is the SPF (Sender Policy Framework) result for this email?

Received-SPF and Authentication-Results agree. The result is a pass, which only says the sending server is authorised for the domain the attacker owns. It does not say the sender is who they claim to be.

python3 -I -c "import email,sys; from email import policy; m=email.message_from_file(open(sys.argv[1]),policy=policy.default); print(m['Received-SPF']); print(m['Authentication-Results'])" evidence/email.eml

Tip: Do not read a passing auth verdict as a trust signal. Judge the domain itself.

Click flag to reveal Pass
Q6 — What is the domain used in the phishing URL inside the email?

Pull the href out of the HTML body part. The link sits on a secure. subdomain of the sender’s own domain, which makes it look consistent with the From address.

python3 -I -c "import email,re,sys; from email import policy; m=email.message_from_file(open(sys.argv[1]),policy=policy.default); print(re.findall(r'href=[\"\\']([^\"\\']+)', m.get_body(('html',)).get_content()))" evidence/email.eml

Gotcha: The format hint says “domain name”, but the registrable domain alone was rejected. The answer is the full host including the secure. label.

Click to reveal answer secure.business-finance.com
Q7 — What is the fake company name used in the email?

The name appears in the message signature and repeats in an X-Organization header, so it is consistent across the message.

python3 -I -c "import email,sys; from email import policy; m=email.message_from_file(open(sys.argv[1]),policy=policy.default); print(m['X-Organization'])" evidence/email.eml
Click flag to reveal Business Finance Ltd.
Q8 — What is the name of the attachment included in the email?

Walk the MIME parts and read the Content-Disposition filename of the attachment part.

python3 -I -c "import email,sys; from email import policy; m=email.message_from_file(open(sys.argv[1]),policy=policy.default); print([p.get_filename() for p in m.iter_attachments()])" evidence/email.eml
Click to reveal answer Invoice_2025_Payment.zip
Q9 — What is the SHA-256 hash of the attachment?

Decode the base64 attachment part and hash the decoded bytes, not the base64 text. The decoded file is only 75 bytes, which is the first sign the archive is truncated (Q10).

python3 -I -c "import email,sys,hashlib; from email import policy; m=email.message_from_file(open(sys.argv[1]),policy=policy.default); a=next(m.iter_attachments()).get_content(); print(len(a), hashlib.sha256(a).hexdigest())" evidence/email.eml
Click flag to reveal 8379c41239e9af845b2ab6c27a7509ae8804d7d73e455c800a551b22ba25bb4a
Q11 — Which MITRE ATT&CK techniques are associated with this attack?

A malicious file delivered as an email attachment is Phishing: Spearphishing Attachment. The format hint shows a single T****.*** ID, so the sub-technique is the answer, not the parent.

Click flag to reveal T1566.001

Defense Evasion

Q10 — What is the filename of the malicious file contained within the ZIP attachment?

unzip -l fails because the base64 in the email is cut short, so the central directory is missing. The local file header at the start of the archive is intact though: the filename length sits at offset 26 and the name follows the 30-byte fixed header. Reading it by hand gives a batch file hiding behind a fake PDF extension. It was identified by name only and never extracted, so its behaviour is not known from this evidence.

python3 -I -c "import struct,sys; d=open(sys.argv[1],'rb').read(); n=struct.unpack('<H',d[26:28])[0]; print(d[30:30+n].decode())" work/att.zip

Tip: When a ZIP will not open, the first local header still gives you the member name without any extraction.

Click to reveal answer invoice_document.pdf.bat

Attack Summary

PhaseAction
Initial AccessVendor-impersonation email sent from attacker-controlled domain, SPF passes
Initial AccessReply-To set to a different mailbox than From
Initial AccessBody links to a secure. lookalike subdomain with an urgent payment request
Initial AccessZIP “invoice” attached
Defense EvasionZIP member is a batch file with a fake .pdf extension (execution not observed)

IOCs

TypeValue
Sender IP45.67.89[.]10
Relay IP203.0.113[.]25
Sender addressfinance@business-finance[.]com
Reply-Tosupport@business-finance[.]com
Link hostsecure[.]business-finance[.]com
AttachmentInvoice_2025_Payment.zip
SHA-256 (attachment)8379c41239e9af845b2ab6c27a7509ae8804d7d73e455c800a551b22ba25bb4a
ZIP memberinvoice_document.pdf.bat

MITRE ATT&CK

TechniqueIDDescription
Phishing: Spearphishing AttachmentT1566.001ZIP attachment carrying a script in an invoice email
Masquerading: Double File ExtensionT1036.007Batch file named to look like a PDF

Defender Takeaways

A passing SPF result is not a trust signal. The attacker registered their own domain and published valid SPF for it, so every auth check passed. Score the sending domain’s age and similarity to known vendors, not just the verdict.

Alert on a Reply-To that differs from From. It is a cheap, high-signal mail rule, especially for finance and accounting mailboxes where payment fraud is the goal.

Quarantine archives containing script files and double-extension names. A .pdf.bat member inside a ZIP should never reach an inbox. Block .bat, .cmd and similar inside archives at the gateway.

Check the full link host, not just the domain. A secure. subdomain on a new vendor domain reads as legitimate. Rewrite and detonate links, and show users the real host.

Make payment changes a process control. Urgent requests from new or changed vendor details need out-of-band confirmation before money moves.

Conclusion

The email carried no exploit and needed none: valid SPF, a plausible vendor domain and a ZIP with a PDF-looking batch file were enough to put a payment lure and a script in front of an accounting team. The detection value was in the header mismatches and the attachment name, not in any authentication result.