1.0.2 update
This commit is contained in:
parent
28a2e61f17
commit
385ba70248
6 changed files with 154 additions and 9 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,4 +1,5 @@
|
|||
build/pwndrop
|
||||
build/pwndrop.exe
|
||||
build/data/
|
||||
build/
|
||||
release/
|
||||
|
|
7
CHANGELOG.md
Normal file
7
CHANGELOG.md
Normal 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.
|
|
@ -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
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
package config
|
||||
|
||||
const Version = "1.0.1"
|
||||
const Version = "1.0.2"
|
||||
|
|
51
core/http.go
51
core/http.go
|
@ -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
|
||||
}
|
||||
|
|
101
core/server.go
101
core/server.go
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue