mirror of
https://github.com/TecharoHQ/anubis.git
synced 2025-08-04 02:08:59 -04:00
lib/anubis: actually check the result with the correct difficulty (#180)
* cmd/anubis actually check the result with the correct difficulty * chore: changelog * test(cmd/anubis): make test check for difficulty * lib: add regression test for CVE-2025-24369 Signed-off-by: Xe Iaso <me@xeiaso.net> * bump VERSION and CHANGELOG Tracks #181 Signed-off-by: Xe Iaso <me@xeiaso.net> --------- Signed-off-by: Xe Iaso <me@xeiaso.net> Co-authored-by: Xe Iaso <me@xeiaso.net>
This commit is contained in:
parent
28828a2e93
commit
b4a2e1a6a0
@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
- Added a periodic cleanup routine for the decaymap that removes expired entries, ensuring stale data is properly pruned.
|
- Added a periodic cleanup routine for the decaymap that removes expired entries, ensuring stale data is properly pruned.
|
||||||
- Added a no-store Cache-Control header to the challenge page
|
- Added a no-store Cache-Control header to the challenge page
|
||||||
- Hide the directory listings for Anubis' internal static content
|
- Hide the directory listings for Anubis' internal static content
|
||||||
@ -28,6 +29,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Fixed a typo in the challenge page title.
|
- Fixed a typo in the challenge page title.
|
||||||
- Disabled running integration tests on Windows hosts due to it's reliance on posix features (see [#133](https://github.com/TecharoHQ/anubis/pull/133#issuecomment-2764732309)).
|
- Disabled running integration tests on Windows hosts due to it's reliance on posix features (see [#133](https://github.com/TecharoHQ/anubis/pull/133#issuecomment-2764732309)).
|
||||||
|
|
||||||
|
## v1.15.1
|
||||||
|
|
||||||
|
Zenos yae Galvus: Echo 1
|
||||||
|
|
||||||
|
Fixes a recurrence of [CVE-2025-24369](https://github.com/Xe/x/security/advisories/GHSA-56w8-8ppj-2p4f)
|
||||||
|
due to an incorrect logic change in a refactor. This allows an attacker to mint a valid
|
||||||
|
access token by passing any SHA-256 hash instead of one that matches the proof-of-work
|
||||||
|
test.
|
||||||
|
|
||||||
|
This case has been added as a regression test. It was not when CVE-2025-24369 was released
|
||||||
|
due to the project not having the maturity required to enable this kind of regression testing.
|
||||||
|
|
||||||
## v1.15.0
|
## v1.15.0
|
||||||
|
|
||||||
Zenos yae Galvus
|
Zenos yae Galvus
|
||||||
|
@ -145,14 +145,13 @@ func New(opts Options) (*Server, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
mux *http.ServeMux
|
mux *http.ServeMux
|
||||||
next http.Handler
|
next http.Handler
|
||||||
priv ed25519.PrivateKey
|
priv ed25519.PrivateKey
|
||||||
pub ed25519.PublicKey
|
pub ed25519.PublicKey
|
||||||
policy *policy.ParsedConfig
|
policy *policy.ParsedConfig
|
||||||
opts Options
|
opts Options
|
||||||
DNSBLCache *decaymap.Impl[string, dnsbl.DroneBLResponse]
|
DNSBLCache *decaymap.Impl[string, dnsbl.DroneBLResponse]
|
||||||
ChallengeDifficulty int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -441,9 +440,9 @@ func (s *Server) PassChallenge(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// compare the leading zeroes
|
// compare the leading zeroes
|
||||||
if !strings.HasPrefix(response, strings.Repeat("0", s.ChallengeDifficulty)) {
|
if !strings.HasPrefix(response, strings.Repeat("0", rule.Challenge.Difficulty)) {
|
||||||
s.ClearCookie(w)
|
s.ClearCookie(w)
|
||||||
lg.Debug("difficulty check failed", "response", response, "difficulty", s.ChallengeDifficulty)
|
lg.Debug("difficulty check failed", "response", response, "difficulty", rule.Challenge.Difficulty)
|
||||||
templ.Handler(web.Base("Oh noes!", web.ErrorPage("invalid response")), templ.WithStatus(http.StatusForbidden)).ServeHTTP(w, r)
|
templ.Handler(web.Base("Oh noes!", web.ErrorPage("invalid response")), templ.WithStatus(http.StatusForbidden)).ServeHTTP(w, r)
|
||||||
failedValidations.Inc()
|
failedValidations.Inc()
|
||||||
return
|
return
|
||||||
|
@ -34,6 +34,79 @@ func spawnAnubis(t *testing.T, opts Options) *Server {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type challenge struct {
|
||||||
|
Challenge string `json:"challenge"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeChallenge(t *testing.T, ts *httptest.Server) challenge {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
resp, err := ts.Client().Post(ts.URL+"/.within.website/x/cmd/anubis/api/make-challenge", "", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("can't request challenge: %v", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
var chall challenge
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&chall); err != nil {
|
||||||
|
t.Fatalf("can't read challenge response body: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return chall
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regression test for CVE-2025-24369
|
||||||
|
func TestCVE2025_24369(t *testing.T) {
|
||||||
|
pol := loadPolicies(t, "")
|
||||||
|
pol.DefaultDifficulty = 4
|
||||||
|
|
||||||
|
srv := spawnAnubis(t, Options{
|
||||||
|
Next: http.NewServeMux(),
|
||||||
|
Policy: pol,
|
||||||
|
|
||||||
|
CookieDomain: "local.cetacean.club",
|
||||||
|
CookiePartitioned: true,
|
||||||
|
CookieName: t.Name(),
|
||||||
|
})
|
||||||
|
|
||||||
|
ts := httptest.NewServer(internal.RemoteXRealIP(true, "tcp", srv))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
chall := makeChallenge(t, ts)
|
||||||
|
calcString := fmt.Sprintf("%s%d", chall.Challenge, 0)
|
||||||
|
calculated := internal.SHA256sum(calcString)
|
||||||
|
nonce := 0
|
||||||
|
elapsedTime := 420
|
||||||
|
redir := "/"
|
||||||
|
|
||||||
|
cli := ts.Client()
|
||||||
|
cli.CheckRedirect = func(req *http.Request, via []*http.Request) error {
|
||||||
|
return http.ErrUseLastResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest(http.MethodGet, ts.URL+"/.within.website/x/cmd/anubis/api/pass-challenge", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("can't make request: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
q := req.URL.Query()
|
||||||
|
q.Set("response", calculated)
|
||||||
|
q.Set("nonce", fmt.Sprint(nonce))
|
||||||
|
q.Set("redir", redir)
|
||||||
|
q.Set("elapsedTime", fmt.Sprint(elapsedTime))
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
|
||||||
|
resp, err := cli.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("can't do challenge passing")
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode == http.StatusFound {
|
||||||
|
t.Log("Regression on CVE-2025-24369")
|
||||||
|
t.Errorf("wanted HTTP status %d, got: %d", http.StatusForbidden, resp.StatusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCookieSettings(t *testing.T) {
|
func TestCookieSettings(t *testing.T) {
|
||||||
pol := loadPolicies(t, "")
|
pol := loadPolicies(t, "")
|
||||||
pol.DefaultDifficulty = 0
|
pol.DefaultDifficulty = 0
|
||||||
@ -72,8 +145,9 @@ func TestCookieSettings(t *testing.T) {
|
|||||||
nonce := 0
|
nonce := 0
|
||||||
elapsedTime := 420
|
elapsedTime := 420
|
||||||
redir := "/"
|
redir := "/"
|
||||||
|
calculated := ""
|
||||||
calcString := fmt.Sprintf("%s%d", chall.Challenge, nonce)
|
calcString := fmt.Sprintf("%s%d", chall.Challenge, nonce)
|
||||||
calculated := internal.SHA256sum(calcString)
|
calculated = internal.SHA256sum(calcString)
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, ts.URL+"/.within.website/x/cmd/anubis/api/pass-challenge", nil)
|
req, err := http.NewRequest(http.MethodGet, ts.URL+"/.within.website/x/cmd/anubis/api/pass-challenge", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user