Post

Flight

Flight

Flight

Overview

Flight is a hard-rated Windows machine. It starts with a website running two virtual hosts, one of which is vulnerable to LFI. We abuse the LFI to grab an NTLMv2 hash and crack it offline.

With the cleartext password in hand, we spray it across a list of valid usernames and find a password-reuse case that gives us another account. That account can write to a share, so we drop a file that forces other users to authenticate to us when they access it, letting us steal a second NTLMv2 hash. We crack that one too and use the credentials to write a PHP webshell onto the share that one of the virtual hosts is mounted on.

Having compromised the web service user, we move to the more privileged account from the second cracked hash and use it to write an ASPX webshell on a development web application that only listens on localhost.

This lands us a shell as the IIS application pool virtual account, which holds the SeImpersonatePrivilege which we abuse to escalate to NT AUTHORITY\SYSTEM.

Pwning

We start off with a nmap scan to identify the open ports

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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
┌──(kali㉿kali)-[~/Desktop/OSCP/Prep/Flight]
└─$ cat 10.129.228.120.scan
# Nmap 7.99 scan initiated Mon Oct  5 14:15:54 2026 as: /usr/lib/nmap/nmap --privileged -vvv -p 53,80,88,135,139,389,445,464,593,636,3269,3268,5985,9389,49667,49673,49674,49699,49737 -4 -A -oN 10.129.228.120.scan -Pn 10.129.228.120
Nmap scan report for G0.flight.htb (10.129.228.120)
Host is up, received user-set (0.21s latency).
Scanned at 2026-10-05 14:15:59 WAT for 113s

PORT      STATE SERVICE       REASON          VERSION
53/tcp    open  domain        syn-ack ttl 127 Simple DNS Plus
80/tcp    open  http          syn-ack ttl 127 Apache httpd 2.4.52 ((Win64) OpenSSL/1.1.1m PHP/8.1.1)
|_http-title: g0 Aviation
| http-methods: 
|   Supported Methods: POST OPTIONS HEAD GET TRACE
|_  Potentially risky methods: TRACE
|_http-server-header: Apache/2.4.52 (Win64) OpenSSL/1.1.1m PHP/8.1.1
88/tcp    open  kerberos-sec  syn-ack ttl 127 Microsoft Windows Kerberos (server time: 2026-10-05 20:15:40Z)
135/tcp   open  msrpc         syn-ack ttl 127 Microsoft Windows RPC
139/tcp   open  netbios-ssn   syn-ack ttl 127 Microsoft Windows netbios-ssn
389/tcp   open  ldap          syn-ack ttl 127 Microsoft Windows Active Directory LDAP (Domain: flight.htb, Site: Default-First-Site-Name)
445/tcp   open  microsoft-ds? syn-ack ttl 127
464/tcp   open  kpasswd5?     syn-ack ttl 127
593/tcp   open  ncacn_http    syn-ack ttl 127 Microsoft Windows RPC over HTTP 1.0
636/tcp   open  tcpwrapped    syn-ack ttl 127
3268/tcp  open  ldap          syn-ack ttl 127 Microsoft Windows Active Directory LDAP (Domain: flight.htb, Site: Default-First-Site-Name)
3269/tcp  open  tcpwrapped    syn-ack ttl 127
5985/tcp  open  http          syn-ack ttl 127 Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
9389/tcp  open  mc-nmf        syn-ack ttl 127 .NET Message Framing
49667/tcp open  msrpc         syn-ack ttl 127 Microsoft Windows RPC
49673/tcp open  ncacn_http    syn-ack ttl 127 Microsoft Windows RPC over HTTP 1.0
49674/tcp open  msrpc         syn-ack ttl 127 Microsoft Windows RPC
49699/tcp open  msrpc         syn-ack ttl 127 Microsoft Windows RPC
49737/tcp open  msrpc         syn-ack ttl 127 Microsoft Windows RPC
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose
Running (JUST GUESSING): Microsoft Windows 2019|10 (97%)
OS CPE: cpe:/o:microsoft:windows_server_2019 cpe:/o:microsoft:windows_10
OS fingerprint not ideal because: Missing a closed TCP port so results incomplete
Aggressive OS guesses: Microsoft Windows Server 2019 (97%), Microsoft Windows 10 1903 - 22H2 (91%)
No exact OS matches for host (test conditions non-ideal).
TCP/IP fingerprint:
SCAN(V=7.99%E=4%D=10/5%OT=53%CT=%CU=%PV=Y%DS=2%DC=T%G=N%TM=6AC3A380%P=x86_64-pc-linux-gnu)
SEQ(SP=105%GCD=1%ISR=10B%TI=I%II=I%SS=S%TS=U)
SEQ(SP=105%GCD=1%ISR=10C%TI=I%II=I%SS=S%TS=U)
OPS(O1=M552NW8NNS%O2=M552NW8NNS%O3=M552NW8%O4=M552NW8NNS%O5=M552NW8NNS%O6=M552NNS)
WIN(W1=FFFF%W2=FFFF%W3=FFFF%W4=FFFF%W5=FFFF%W6=FF70)
ECN(R=Y%DF=Y%TG=80%W=FFFF%O=M552NW8NNS%CC=Y%Q=)
T1(R=Y%DF=Y%TG=80%S=O%A=S+%F=AS%RD=0%Q=)
T2(R=N)
T3(R=N)
T4(R=N)
U1(R=N)
IE(R=Y%DFI=N%TG=80%CD=Z)

Network Distance: 2 hops
TCP Sequence Prediction: Difficulty=261 (Good luck!)
IP ID Sequence Generation: Incremental
Service Info: Host: G0; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb2-time: 
|   date: 2026-10-05T20:16:40
|_  start_date: N/A
| smb2-security-mode: 
|   3.1.1: 
|_    Message signing enabled and required
| p2p-conficker: 
|   Checking for Conficker.C or higher...
|   Check 1 (port 50845/tcp): CLEAN (Timeout)
|   Check 2 (port 32149/tcp): CLEAN (Timeout)
|   Check 3 (port 21917/udp): CLEAN (Timeout)
|   Check 4 (port 28337/udp): CLEAN (Timeout)
|_  0/4 checks are positive: Host is CLEAN or ports are blocked
|_clock-skew: 6h59m31s

TRACEROUTE (using port 139/tcp)
HOP RTT       ADDRESS
1   209.55 ms 10.10.14.1
2   209.75 ms G0.flight.htb (10.129.228.120)

Read data files from: /usr/share/nmap
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Mon Oct  5 14:17:52 2026 -- 1 IP address (1 host up) scanned in 118.65 seconds

From the presence of:

  • 53 (DNS)
  • 88 (Kerberos)

we can tell this is an Active Directory domain controller.

NetExec (https://github.com/Pennyw0rth/NetExec) can be used to enumerate the host over SMB and pull its FQDN and domain name, which we then add to our local /etc/hosts file.

nxc

It’s also important to sync our local clock with the DC to avoid Kerberos clock-skew issues.

1
2
3
4
┌──(kali㉿kali)-[~/Desktop/OSCP/Prep/Flight]
└─$ sudo ntpdate flight.htb
2026-10-05 23:55:37.302022 (+0100) +678.814538 +/- 0.117473 flight.htb 10.129.101.114 s1 no-leap
CLOCK: time stepped by 678.814538

Starting with enumeration, our attack surface isn’t large since we don’t have any valid domain credentials yet, so we begin by authenticating to the services as an anonymous user.

With the SMB and LDAP services running, I tried to authenticate using null credentials.

smb1 smb2

That doesn’t work, so our focus shifts fully to the web application.

Accessing it shows that it’s an Airline Flight Planner.

web1

The page is pretty much static and from the fingerprinting nmap did, we know that this is an Apache web server running PHP/8.1.1

I went ahead and fuzzed for files and directories but got nothing. Fuzzing for virtual hosts, however, gave me school.flight.htb.

web2 web3 web4

After poking around the application, I noticed it loads pages through a file-inclusion parameter (e.g. ?view=), which immediately hints at a possible LFI.

web4 web4

Attempts to traverse shows it’s filtered and blocked.

web4

Since I have no idea what filtering is in place, we can either try a few characters manually to see which ones get blocked, or fuzz the parameter with a payload list and see which entries succeed.

web8

A useful observation from the fuzz output is that the filter doesn’t block any file read as long as the Windows path separator \ isn’t used… but that’s not much of a restriction, since forward slashes / work just as well on Windows for path traversal.

We could have also just read the source code index.php:

web9

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php

ini_set('display_errors', 0);
error_reporting(E_ERROR | E_WARNING | E_PARSE); 

if(isset($_GET['view'])){
$file=$_GET['view'];
if ((strpos(urldecode($_GET['view']),'..')!==false)||
    (strpos(urldecode(strtolower($_GET['view'])),'filter')!==false)||
    (strpos(urldecode($_GET['view']),'\\')!==false)||
    (strpos(urldecode($_GET['view']),'htaccess')!==false)||
    (strpos(urldecode($_GET['view']),'.shtml')!==false)
){
    echo "<h1>Suspicious Activity Blocked!";
    echo "<h3>Incident will be reported</h3>\r\n";
}else{
    echo file_get_contents($_GET['view']);	
}
}else{
    echo file_get_contents("C:\\xampp\\htdocs\\school.flight.htb\\home.html");
}
	
?>

This confirms our assumption, as we now know the filter in place.

Since the function file_get_contents is used, this makes getting RCE tough because it only gives us limited file read primitive.

I read the php.ini file to see if the dangerous option allow_url_include is enabled but unfortunately it isn’t.

web10

Click to expand the full php.ini ```text ┌──(kali㉿kali)-[~/Desktop/OSCP/Prep/Flight] └─$ grep -Ev '^\s*(;|$)' a.txt engine = On short_open_tag = Off precision = 14 output_buffering = 4096 zlib.output_compression = Off implicit_flush = Off unserialize_callback_func = serialize_precision = -1 disable_functions = disable_classes = zend.enable_gc = On zend.exception_ignore_args = Off zend.exception_string_param_max_len = 15 expose_php = On max_execution_time = 120 max_input_time = 60 memory_limit = 512M error_reporting = E_ALL display_errors = On display_startup_errors = On log_errors = On ignore_repeated_errors = Off ignore_repeated_source = Off report_memleaks = On variables_order = "GPCS" request_order = "GP" register_argc_argv = Off auto_globals_jit = On post_max_size = 40M auto_prepend_file = auto_append_file = default_mimetype = "text/html" default_charset = "UTF-8" include_path = \xampp\php\PEAR doc_root = user_dir = extension_dir = "\xampp\php\ext" enable_dl = Off file_uploads = On upload_tmp_dir = "\xampp\tmp" upload_max_filesize = 40M max_file_uploads = 20 allow_url_fopen = On allow_url_include = Off default_socket_timeout = 60 extension=bz2 extension=curl extension=fileinfo extension=gettext extension=mbstring extension=exif ; Must be after mbstring as it depends on it extension=mysqli extension=pdo_mysql extension=pdo_sqlite asp_tags=Off display_startup_errors=On track_errors=Off y2k_compliance=On allow_call_time_pass_reference=Off safe_mode=Off safe_mode_gid=Off safe_mode_allowed_env_vars=PHP_ safe_mode_protected_env_vars=LD_LIBRARY_PATH error_log="\xampp\php\logs\php_error_log" register_globals=Off register_long_arrays=Off magic_quotes_gpc=Off magic_quotes_runtime=Off magic_quotes_sybase=Off extension=php_openssl.dll extension=php_ftp.dll [CLI Server] cli_server.color = On [Date] [filter] [iconv] [imap] [intl] [sqlite3] [Pcre] [Pdo] pdo_mysql.default_socket="MySQL" [Pdo_mysql] pdo_mysql.default_socket= [Phar] [mail function] SMTP = localhost smtp_port = 25 mail.add_x_header = Off [ODBC] odbc.allow_persistent = On odbc.check_persistent = On odbc.max_persistent = -1 odbc.max_links = -1 odbc.defaultlrl = 4096 odbc.defaultbinmode = 1 [MySQLi] mysqli.max_persistent = -1 mysqli.allow_persistent = On mysqli.max_links = -1 mysqli.default_port = 3306 mysqli.default_socket = mysqli.default_host = mysqli.default_user = mysqli.default_pw = mysqli.reconnect = Off [mysqlnd] mysqlnd.collect_statistics = On mysqlnd.collect_memory_statistics = On [OCI8] [PostgreSQL] pgsql.allow_persistent = On pgsql.auto_reset_persistent = Off pgsql.max_persistent = -1 pgsql.max_links = -1 pgsql.ignore_notice = 0 pgsql.log_notice = 0 [bcmath] bcmath.scale = 0 [browscap] browscap = "\xampp\php\extras\browscap.ini" [Session] session.save_handler = files session.save_path = "\xampp\tmp" session.use_strict_mode = 0 session.use_cookies = 1 session.use_only_cookies = 1 session.name = PHPSESSID session.auto_start = 0 session.cookie_lifetime = 0 session.cookie_path = / session.cookie_domain = session.cookie_httponly = session.cookie_samesite = session.serialize_handler = php session.gc_probability = 1 session.gc_divisor = 1000 session.gc_maxlifetime = 1440 session.referer_check = session.cache_limiter = nocache session.cache_expire = 180 session.use_trans_sid = 0 session.sid_length = 26 session.trans_sid_tags = "a=href,area=href,frame=src,form=" session.sid_bits_per_character = 5 [Assertion] zend.assertions = 1 [COM] [mbstring] [gd] [exif] [Tidy] tidy.clean_output = Off [soap] soap.wsdl_cache_enabled=1 soap.wsdl_cache_dir="/tmp" soap.wsdl_cache_ttl=86400 soap.wsdl_cache_limit = 5 [sysvshm] [ldap] ldap.max_links = -1 [dba] [opcache] [curl] curl.cainfo = "\xampp\apache\bin\curl-ca-bundle.crt" [openssl] openssl.cafile = "\xampp\apache\bin\curl-ca-bundle.crt" [ffi] [Syslog] define_syslog_variables=Off [Session] define_syslog_variables=Off [Date] date.timezone=Europe/Berlin [MySQL] mysql.allow_local_infile=On mysql.allow_persistent=On mysql.cache_size=2000 mysql.max_persistent=-1 mysql.max_link=-1 mysql.default_port=3306 mysql.default_socket="MySQL" mysql.connect_timeout=3 mysql.trace_mode=Off [Sybase-CT] sybct.allow_persistent=On sybct.max_persistent=-1 sybct.max_links=-1 sybct.min_server_severity=10 sybct.min_client_severity=10 [MSSQL] mssql.allow_persistent=On mssql.max_persistent=-1 mssql.max_links=-1 mssql.min_error_severity=10 mssql.min_message_severity=10 mssql.compatability_mode=Off mssql.secure_connection=Off ```

I also read the virtual host configuration file to check for any vhosts I might have missed. It’s located at C:/xampp/apache/conf/extra/httpd-vhosts.conf

1
2
3
4
5
6
7
8
<VirtualHost *:80>
    DocumentRoot "C:\xampp\htdocs\flight.htb"
    ServerName flight.htb
</VirtualHost>
<VirtualHost *:80>
    DocumentRoot "C:\xampp\htdocs\school.flight.htb"
    ServerName school.flight.htb
</VirtualHost>
1
svc_apache:S@Ss!K@*t13
1
c.bum:Tikkycoll_431012284
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
> GET / HTTP/1.1
> Host: 127.0.0.1:8000
> User-Agent: curl/7.79.1
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 404 Not Found
< Cache-Control: private
< Content-Type: text/html; charset=utf-8
< Server: Microsoft-IIS/10.0
< X-Powered-By: ASP.NET
< Date: Mon, 05 Oct 2026 22:04:43 GMT
< Content-Length: 4848
< 
* Connection #0 to host 127.0.0.1 left intact
This post is licensed under CC BY 4.0 by the author.