1.0.2 update

This commit is contained in:
Kuba Gretzky 2020-10-01 19:32:01 +02:00
parent 28a2e61f17
commit 385ba70248
6 changed files with 154 additions and 9 deletions

1
.gitignore vendored
View file

@ -1,4 +1,5 @@
build/pwndrop
build/pwndrop.exe
build/data/
build/
release/

7
CHANGELOG.md Normal file
View file

@ -0,0 +1,7 @@
****v1.0.2****
- [x] Unauthorized connections not pointing to any hosted files, returning 404, are now automatically closed instead of being kept alive. This resolves the issue of pwndrop getting DDoSed quickly with bots hammering requests at it from various sources.
- [x] Anti-DDoS feature has been added, which temporarily blacklists every IP address of a client who made 10 consecutive requests returning 404. Blacklist period is currently 10 minutes.
- [x] Removed timeouts for uploading and downloading files fully. The previous 15 minutes timeout would have not helped with DDoS attacks anyway.
****v1.0.1****
- [x] Increased the time limit for uploads and downloads from 15 seconds to 15 minutes. Should fix the issue of uploads/downloads being interrupted on slow connections, when handling big files.

View file

@ -87,6 +87,7 @@ First of all, make sure you have installed GO with version at least **1.13**: ht
Then do the following:
```
sudo apt-get -y install git make
git clone https://github.com/kgretzky/pwndrop
cd pwndrop
make

View file

@ -1,3 +1,3 @@
package config
const Version = "1.0.1"
const Version = "1.0.2"

View file

@ -1,14 +1,25 @@
package core
import (
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"strings"
"time"
"github.com/kgretzky/pwndrop/log"
)
const BLACKLIST_JAIL_TIME_SECS = 10 * 60
const BLACKLIST_HITS_LIMIT = 10
type BlacklistItem struct {
hits int
last_hit time.Time
}
type Http struct {
srv *Server
}
@ -22,15 +33,25 @@ func NewHttp(srv *Server) (*Http, error) {
func (s *Http) ServeHTTP(w http.ResponseWriter, r *http.Request) {
data_dir := Cfg.GetDataDir()
from_ip := r.RemoteAddr
if strings.Contains(from_ip, ":") {
from_ip = strings.Split(from_ip, ":")[0]
}
if r.Method == "GET" {
f, status, err := s.srv.GetFile(r.URL.Path)
if err != nil {
w.WriteHeader(status)
log.Error("http: get: %s: %s", r.URL.Path, err)
log.Error("http: get: %s: %s (%s)", r.URL.Path, err, from_ip)
err := s.killConnection(w, status)
if err != nil {
log.Error("http: %s (%s)", err, from_ip)
}
return
}
if f.RedirectPath != "" && f.RedirectPath != r.URL.Path && !f.IsPaused {
log.Error("http: get: %s: redirecting to '%s' (%s)", r.URL.Path, f.RedirectPath, from_ip)
http.Redirect(w, r, f.RedirectPath, http.StatusFound)
} else {
mime_type := f.MimeType
@ -41,7 +62,7 @@ func (s *Http) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fo, err := os.Open(fpath)
//data, err := ioutil.ReadFile(fpath)
if err != nil {
log.Error("http: file: %s: %s", f.Filename, err)
log.Error("http: file: %s: %s (%s)", f.Filename, err, from_ip)
return
}
defer fo.Close()
@ -52,5 +73,27 @@ func (s *Http) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
return
}
w.WriteHeader(404)
err := s.killConnection(w, 404)
if err != nil {
log.Error("http: %s (%s)", err, from_ip)
}
}
func (s *Http) killConnection(w http.ResponseWriter, status int) error {
if status > 0 {
w.Header().Set("Connection", "close")
w.Header().Set("Content-Length", "0")
w.WriteHeader(status)
}
hj, ok := w.(http.Hijacker)
if !ok {
return fmt.Errorf("connection hijacking not supported")
}
conn, _, err := hj.Hijack()
if err != nil {
return err
}
conn.Close()
return nil
}

View file

@ -7,6 +7,7 @@ import (
"net/http"
"path/filepath"
"strings"
"sync"
"time"
"github.com/gorilla/mux"
@ -30,11 +31,16 @@ type Server struct {
cdb *CertDb
ns *Nameserver
r *mux.Router
blacklist map[string]*BlacklistItem
bl_mtx sync.Mutex
}
func NewServer(host string, port_plain int, port_tls int, enable_letsencrypt bool, enable_dns bool, ch_exit *chan bool) (*Server, error) {
var err error
s := &Server{}
s := &Server{
blacklist: make(map[string]*BlacklistItem),
bl_mtx: sync.Mutex{},
}
hostname := fmt.Sprintf("%s:%d", host, port_plain)
hostname_tls := fmt.Sprintf("%s:%d", host, port_tls)
@ -70,6 +76,23 @@ func NewServer(host string, port_plain int, port_tls int, enable_letsencrypt boo
log.Info("autocert: disabled")
}
// set up modern cipher suites
/*
tls_cfg.MinVersion = tls.VersionTLS12
tls_cfg.CipherSuites = []uint16{
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, // Go 1.8 only
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, // Go 1.8 only
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
// Best disabled, as they don't provide Forward Secrecy,
// but might be necessary for some clients
// tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
// tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
}*/
s.wdav, err = NewWebDav(s)
if err != nil {
return nil, err
@ -84,9 +107,9 @@ func NewServer(host string, port_plain int, port_tls int, enable_letsencrypt boo
s.srv = &http.Server{
Handler: http.Handler(s),
Addr: hostname,
WriteTimeout: 15 * time.Minute,
ReadTimeout: 15 * time.Minute,
IdleTimeout: 120 * time.Second,
WriteTimeout: 0,
ReadTimeout: 0,
IdleTimeout: 5 * time.Second,
TLSConfig: tls_cfg,
}
@ -131,6 +154,22 @@ func NewServer(host string, port_plain int, port_tls int, enable_letsencrypt boo
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
log.Debug("%s %s", r.Method, r.URL.Path)
from_ip := r.RemoteAddr
if strings.Contains(from_ip, ":") {
from_ip = strings.Split(from_ip, ":")[0]
}
if s.isBlacklisted(from_ip) {
err := s.killConnection(w, -1)
if err != nil {
log.Error("http: %s (%s)", err, from_ip)
w.Header().Set("Connection", "close")
w.Header().Set("Content-Length", "0")
w.WriteHeader(500)
}
return
}
if !s.isWebDavRequest(r) {
cookie_name := Cfg.GetCookieName()
@ -158,6 +197,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
}
s.addBlacklistHit(from_ip)
if len(Cfg.GetRedirectUrl()) > 0 {
http.Redirect(w, r, Cfg.GetRedirectUrl(), http.StatusFound)
return
@ -260,3 +300,56 @@ func (s *Server) FileExists(url string) bool {
}
return true
}
func (s *Server) killConnection(w http.ResponseWriter, status int) error {
if status > 0 {
w.Header().Set("Connection", "close")
w.Header().Set("Content-Length", "0")
w.WriteHeader(status)
}
hj, ok := w.(http.Hijacker)
if !ok {
return fmt.Errorf("connection hijacking not supported")
}
conn, _, err := hj.Hijack()
if err != nil {
return err
}
conn.Close()
return nil
}
func (s *Server) isBlacklisted(ip_addr string) bool {
s.bl_mtx.Lock()
defer s.bl_mtx.Unlock()
ret := false
if bl, ok := s.blacklist[ip_addr]; ok {
if bl.hits >= BLACKLIST_HITS_LIMIT {
if time.Now().Before(bl.last_hit.Add(BLACKLIST_JAIL_TIME_SECS * time.Second)) {
ret = true
} else {
delete(s.blacklist, ip_addr)
return false
}
}
bl.last_hit = time.Now()
}
return ret
}
func (s *Server) addBlacklistHit(ip_addr string) {
s.bl_mtx.Lock()
defer s.bl_mtx.Unlock()
if bl, ok := s.blacklist[ip_addr]; ok {
bl.hits += 1
} else {
bl := &BlacklistItem{
hits: 1,
last_hit: time.Now(),
}
s.blacklist[ip_addr] = bl
}
}