Out of bounds write in resolved with crafted TCP responses
Affects | Status | Importance | Assigned to | Milestone | |
---|---|---|---|---|---|
systemd (Ubuntu) |
Fix Released
|
High
|
Dimitri John Ledkov | ||
Xenial |
Fix Released
|
Undecided
|
Unassigned | ||
Yakkety |
Fix Released
|
Undecided
|
Unassigned | ||
Zesty |
Fix Released
|
Undecided
|
Unassigned | ||
Artful |
Fix Released
|
High
|
Dimitri John Ledkov |
Bug Description
[Impact]
Certain sizes passed to dns_packet_new can cause it to allocate a buffer that's too small. A page-aligned number - sizeof(DnsPacket) + sizeof(iphdr) + sizeof(udphdr) will do this - so, on x86 this will be a page-aligned number - 80. Eg, calling dns_packet_new with a size of 4016 will result in an allocation of 4096 bytes, but 108 bytes of this are for the DnsPacket struct.
A malicious DNS server can exploit this by responding with a specially crafted TCP payload to trick systemd-resolved in to allocating a buffer that's too small, and subsequently write arbitrary data beyond the end of it.
To demonstrate this you can run the attached python script. This is a mock DNS server that sends a response where the first two bytes of the TCP payload specify a size of 4016 (note, this size is picked to trigger an out of bounds write on x86 - you'll probably need to pick a different number for x86-64). You'll need to temporarily set your DNS server to 127.0.0.1.
[Testcase]
Launch the attached script on i386, point resolved at the started dns server, execute a dns query via resolved observe that it crashes.
Upgrade systemd package and observe that resolved no longer crashes.
[Regression Potential]
Low, resolved is not used by default in xenial. This is a bug fix to resolved, in case somebody does use resolved in xenial.
CVE References
description: | updated |
Changed in systemd (Ubuntu): | |
assignee: | nobody → Dimitri John Ledkov (xnox) |
importance: | Undecided → High |
milestone: | none → ubuntu-17.06 |
information type: | Private Security → Public Security |
Changed in systemd (Ubuntu Xenial): | |
status: | New → Confirmed |
Changed in systemd (Ubuntu Yakkety): | |
status: | New → Fix Released |
Changed in systemd (Ubuntu Zesty): | |
status: | New → Fix Released |
description: | updated |
The test case results in traces like this.
#0 0xb7758cf9 in __kernel_vsyscall () signal_ restore_ set (set=0xbf8395e0) at ../sysdeps/ unix/sysv/ linux/nptl- signals. h:79 unix/sysv/ linux/raise. c:55 handler = {sa_handler = 0x30320a5d, sa_sigaction = 0x30320a5d}, sa_mask = {__val = {908996910, 892822026, 808465971, 929180976, 808923957, 1914712112, 544222509, 1664102448, 808464481, 979592736, 840970544, 859255606, 538976310, 1815027744, 1764713065, 758528051, 1970170220, 1852255608, 1768697717, 1919117154, 779382905, 841903987, 774975024, 929172022, 808923957, 1647128624, 1630745911, 540028976, 1882027890, 808464416, 0, 4096}}, sa_flags = -1222154106, sa_restorer = 0xbf839840} <optimised out>, fmt=<optimised out>) at ../sysdeps/ posix/libc_ fatal.c: 175 free.lto_ priv.128 (s=<optimised out>) at ../src/ libsystemd/ sd-event/ sd-event. c:887 source_ unref (s=<optimised out>) at ../src/ libsystemd/ sd-event/ sd-event. c:1402 stop.lto_ priv.71 (s=<optimised out>) at ../src/ resolve/ resolved- dns-stream. c:35 resolve/ resolved- dns-stream. c:55 io.lto_ priv.64 (es=0x809d6640, fd=18, revents=1, userdata= 0x809d6738) at ../src/ resolve/ resolved- dns-stream. c:321
No symbol table info available.
#1 0xb728b050 in __libc_
No locals.
#2 __GI_raise (sig=6) at ../sysdeps/
set = {__val = {18946, 0, 808464438, 926376493, 808466485, 762454064, 807432237, 808464432, 540028976, 809119792, 540024880, 538976288, 538976288, 1987468064, 173896289, 892811106, 808464440, 926376493, 808476981, 762454064, 807432312, 808464432, 540028976, 809119792, 540024880, 538976288, 538976288, 1685478176, 173895539, 892811106, 808464481, 926376493}}
pid = <optimised out>
tid = <optimised out>
ret = 0
#3 0xb728c577 in __GI_abort () at abort.c:89
save_stage = 2
act = {__sigaction_
sigs = {__val = {32, 0 <repeats 31 times>}}
#4 0xb72c6f4f in __libc_message (do_abort=
ap = <optimised out>
fd = 2
on_2 = <optimised out>
list = <optimised out>
nlist = <optimised out>
cp = <optimised out>
written = <optimised out>
#5 0xb72cdb47 in malloc_printerr (action=<optimised out>, str=0xb73c2d5c "double free or corruption (out)", ptr=<optimised out>, ar_ptr=0xb7415780 <main_arena>) at malloc.c:5046
buf = "809d68b0"
cp = <optimised out>
ar_ptr = 0xb7415780 <main_arena>
ptr = <optimised out>
str = 0xb73c2d5c "double free or corruption (out)"
action = <optimised out>
#6 0xb72ce406 in _int_free (av=0xb7415780 <main_arena>, p=0x809d68a8, have_lock=0) at malloc.c:3902
size = <optimised out>
fb = <optimised out>
nextchunk = <optimised out>
nextsize = <optimised out>
nextinuse = <optimised out>
prevsize = <optimised out>
bck = <optimised out>
fwd = <optimised out>
errstr = <optimised out>
locked = <optimised out>
__func__ = "_int_free"
#7 0xb75a76fe in source_
No locals.
#8 0xb7602507 in sd_event_
No locals.
#9 0x800523b6 in dns_stream_
No locals.
#10 0x8005240b in dns_stream_complete (s=<optimised out>, error=<optimised out>) at ../src/
No locals.
#11 0x80052689 in on_stream_
s = 0x80...