Skip to main content

Scenario description library for oidc scenario based tester

Project description

osbtlib

Github GitHub osbt osbt

Scenario description library for oidc-scenario-based-tester/osbt

Install

https://pypi.org/project/osbtlib/

pip install osbtlib

Setup

Installation of Python, pip and oidc-scenario-based-tester/osbt is required.

Quick Start

Write a test scenario script.

$ touch scenario.py
import sys
import os
import time
import jwt

from osbtlib import BrowserSimulator, Osbtlib

# Test Information
test_name = "IDSpoofing"
test_description = "- The attacker op modifies the id_token to impersonate the victim <br> - The sub claim of the id_token is modified to the victim's sub claim"
outcome = "failed"
err_msg = ""
countermeasure = "- Check the signature of the id_token <br> - Check the iss claim of the id_token <br> - Check the sub claim of the id_token"


ATTACKER_OP_ENDPOINT = "http://localhost:9997"
HONEST_RP_ENDPOINT = "http://localhost:9999"
PROXY_SERVER_ENDPOINT = "http://localhost:8080"

osbt = Osbtlib(
    attacker_op_url = ATTACKER_OP_ENDPOINT
)

bs = BrowserSimulator(f'{HONEST_RP_ENDPOINT}/login?issuer={ATTACKER_OP_ENDPOINT}/', PROXY_SERVER_ENDPOINT)

try:
    # create malicious id_token
    malicious_id_token = jwt.encode({"issuer": "http://localhost:9997/", "sub": "hoge"}, key="", algorithm="none")

    # send order to attacker op
    res = osbt.attacker_op.replace_id_token(malicious_id_token)
    print("request sent:", res)

    time.sleep(5)

    # victim credentials
    victim_username = 'test-user@localhost'
    victim_password = 'verysecure'

    # browser simulation
    sso_flow = f"""
page.locator('input[name="username"]').fill('{victim_username}')
page.locator('input[name="password"]').fill('{victim_password}')
page.locator('button[type="submit"]').click()
print(page.content())
    """
    
    bs.run(sso_flow)
    content = bs.get_content()
    print("content:", content)
    bs.close()

    # result check
    if "issuer does not match" in content:
        outcome = "pass"

    osbt.attacker_op.clean()

    osbt.cli.send_result(test_name, test_description, outcome, err_msg, countermeasure)
except Exception as e:
    print('Error:', e)
    osbt.attacker_op.clean()

    outcome = "failed"
    err_msg = str(e)
    osbt.cli.send_result(test_name, test_description, outcome, err_msg, countermeasure)

Run example RP and attacker OP.

# start oidc op server
$ attacker-op

# start oidc web client (in a new terminal)
$ git clone https://github.com/oidc-scenario-based-tester/osbt.git
$ cd osbt
$ CLIENT_ID=web CLIENT_SECRET=secret ISSUER=http://localhost:9997/ SCOPES="openid profile" PORT=9999 go run github.com/oidc-scenario-based-tester/osbt/oidc/rp/user-selected

Run osbt server and proxy server extension.

$ osbt server
$ mitmdump --ssl-insecure -s proxy-extension.py

Then execute scenario script by osbt run.

$ osbt run -f scenario.py -t 30s

Usage

Import

import Osbtlib, BrowserSimulator from osbtlib

Osbtlib includes a client that interacts with proxy extensions, a client that interacts with the Attacker OP, and the ability to manipulate ID Token.

BrowserSimulator includes the ability to automate browser operations using PlayWright.

osbt = Osbtlib(
    proxy_extension_url = "http://localhost:5555",
    attacker_op_url = "http://localhost:9997",
    cli_server_url = "http://localhost:54454"
)

bs = BrowserSimulator(
    url = "http://localhost:9999/login",
    proxy_url = "http://localhost:8080"
)

Create an instance of Osbtlib. The arguments are as follows:

  • proxy_extension_url : URL of proxy extension server
  • attacker_op_url: URL of attacker op
  • cli_server_url: URL of osbt server

Create an instance of BrowserSimulator. The arguments are as follows:

  • url: URL of login page
  • proxy_url: URL of proxy server

Proxy Server Operation Osbtlib.proxy

add, modify request header

osbt.proxy.add_header("header_name", "header_value", "host", "path", "method")
osbt.proxy.modify_header("modified_header_name", "header_value", "host", "path", "method")

add_header(name: str, value: str, host: str = None, path: str = None, method: str = None) -> dict

  • name: Name of the header to be added
  • value: Value of the header to be added
  • domain: Condition1, host name of the request
  • path: Condition2, path of the request
  • method: Condition3, method of the request

If you specify conditions, the header will be added if the conditions are met.

modify_header(name: str, value: str, host: str = None, path: str = None, method: str = None) -> dict

  • name: Name of the header to be replaced
  • value: Value of the header to be replaced
  • domain: Condition1, host name of the request
  • path: Condition2, path of the request
  • method: Condition3, method of the request

If you specify conditions, the header will be modified if the conditions are met.

add, modify request query param

osbt.proxy.add_query_param("param_name", "param_value", "host", "path", "method")
osbt.proxy.modify_query_param("modified_param_name", "param_value", "host", "path", "method")

add_query_param(name: str, value: str, host: str = None, path: str = None, method: str = None) -> dict

  • name: Name of the query param to be added
  • value: Value of the query param to be added
  • domain: Condition1, host name of the request
  • path: Condition2, path of the request
  • method: Condition3, method of the request

If you specify conditions, the query param will be added if the conditions are met.

modify_query_param(name: str, value: str, host: str = None, path: str = None, method: str = None) -> dict

  • name: Name of the query param to be replaced
  • value: Value of the query param to be replaced
  • domain: Condition1, host name of the request
  • path: Condition2, path of the request
  • method: Condition3, method of the request

If you specify conditions, the query param will be modified if the conditions are met.

add, modify request body param

osbt.proxy.add_body_param("param_name", "param_value", "host", "path", "method")
osbt.proxy.modify_body_param("modified_param_name", "param_value", "host", "path", "method")

add_body_param(name: str, value: str, host: str = None, path: str = None, method: str = None) -> dict

  • name: Name of the body param to be added
  • value: Value of the body param to be added
  • domain: Condition1, host name of the request
  • path: Condition2, path of the request
  • method: Condition3, method of the request

If you specify conditions, the body param will be added if the conditions are met.

modify_body_param(name: str, value: str, host: str = None, path: str = None, method: str = None) -> dict

  • name: Name of the body param to be replaced
  • value: Value of the body param to be replaced
  • domain: Condition1, host name of the request
  • path: Condition2, path of the request
  • method: Condition3, method of the request

If you specify conditions, the body param will be modified if the conditions are met.

intercept request/response

osbt.proxy.intercept_request("condition")
osbt.proxy.intercept_response("condition")

intercept_request(host: str = None, path: str = None, method: str = None) -> dict

  • condition: Requests containing this string are dropped
  • domain: Condition1, host name of the request
  • path: Condition2, path of the request
  • method: Condition3, method of the request

If you specify conditions, the request will be intercepted if the conditions are met.

intercept_response(host: str = None, path: str = None, method: str = None) -> dict

  • condition: Responses containing this string are dropped
  • domain: Condition1, host name of the request
  • path: Condition2, path of the request
  • method: Condition3, method of the request

If you specify conditions, the response will be intercepted if the conditions are met.

get request/response history

osbt.proxy.get_history()

get_history() -> dict

delete all rules and history

osbt.proxy.clean()

clean() -> dict

Attacker OP Operation Osbtlib.attacker-op

ID Token Replacement for Responses

osbt.attacker_op.replace_id_token("[id_token]")

replace_id_token(id_token: str) -> bool

  • id_token: The value after replacement of the ID token in the response from the Attacker OP.

Providing malicious endpoints using the Discovery service

osbt.attacker_op.set_malicious_endpoints({
    "authorization_endpoint": "http://localhost:9999/auth"
})

set_malicious_endpoints(endpoints: dict) -> bool

  • endpoints: Change the endpoints that Attacker OP returns when /.well-known/openid-configuration is accessed. The following endpoints can be changed.
    • authorization_endpoint
    • token_endpoint
    • userinfo_endpoint
    • registration_endpoint

Redirect to Honest OP upon an authentication request

osbt.attacker_op.idp_confusion("http://localhost:9998/auth")

idp_confusion(honest_op_auth_endpoint: str) -> bool

  • honest_op_auth_endpoint: authorization_endpoint of the honest OP. When an authentication request is sent to the Attacker OP, it is redirected to this endpoint with the query parameters passed on.

Attacker Server Operation Osbtlib.requestbin

Get Requestbin history

osbt.requestbin.get_requestbin_history("PIPEDREAM_TOKEN", "PIPEDREAM_SOURCE_ID")

get_requestbin_history(pipedream_token: str, source_id: str)

  • pipedream_token: Pipedream API token
  • source_id: Pipedream source ID

You can use requestbin of pipedream as attacker server.

Browser Simulator Operation BrowserSimulator

Execute SSO Flow

sso_flow = f"""
page.locator('input[name="username"]').fill('{victim_username}')
page.locator('input[name="password"]').fill('{victim_password}')
page.locator('button[type="submit"]').click()
print(page.content())
"""
bs.run(sso_flow)

bs.run(script: str)

  • script: Script that shows browser behavior for executing SSO flow, written in PlayWright.

Visit the specified URL

bs.visit("https://example.com")

bs.visit(url: str)

  • url: URL to visit in browser.

Retrieving the content displayed in the browser

bs.get_content() 

bs.get_content() -> str

Close the browser

bs.close()

bs.close()

CLI Operation Osbtlib.cli

send test result to osbt server

osbt.cli.send_result(
    "IDSpoofing",
    "- The attacker op modifies the id_token to impersonate the victim <br> - The sub claim of the id_token is modified to the victim's sub claim",
    "Passed",
    "",
    "- Check the signature of the id_token <br> - Check the iss claim of the id_token <br> - Check the sub claim of the id_token"
)

send_result(test_name: str, description: str, outcome: str, err_msg: str, countermeasure: str) -> dict

Others

ID Token Operation Osbtlib.id_token

get, replace header
osbt.id_token.get_header("[id_token]") # {'alg': 'HS256', 'typ': 'JWT'}
osbt.id_token.replace_header("[id_token]", {'alg': 'HS256', 'typ': 'JWS'}) 

get_header(id_token: str) -> dict

  • id_token: ID token for which the header will be obtained.

replace_header(id_token: str, new_header: dict) -> str

  • id_token: ID token for which the header will be replaced.
  • new_header: Header of ID token after replacement.
get, replace payload
osbt.id_token.get_payload("[id_token]") # {'sub': '1234', 'username': 'guest'}
osbt.id_token.replace_payload("[id_token]", {'sub': '1234', 'username': 'guest'}) 

get_payload(id_token: str) -> dict

  • id_token: ID token for which the payload will be obtained.

replace_payload(id_token: str, new_payload: dict) -> str

  • id_token: ID token for which the payload will be replaced.
  • new_payload: Payload of ID token after replacement.
get, replace signature
osbt.id_token.get_signature("[id_token]") # SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
osbt.id_token.replace_header("[id_token]", "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c") 

get_signature(id_token: str) -> str

  • id_token: ID token for which the signature will be obtained.

replace_header(id_token: str, new_signature: str) -> str

  • id_token: ID token for which the signature will be replaced.
  • new_signature: Header of ID token after replacement.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

osbtlib-0.0.4.tar.gz (12.8 kB view details)

Uploaded Source

Built Distribution

osbtlib-0.0.4-py3-none-any.whl (13.2 kB view details)

Uploaded Python 3

File details

Details for the file osbtlib-0.0.4.tar.gz.

File metadata

  • Download URL: osbtlib-0.0.4.tar.gz
  • Upload date:
  • Size: 12.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.9.18

File hashes

Hashes for osbtlib-0.0.4.tar.gz
Algorithm Hash digest
SHA256 9bece0ec75dc7e509df85cf58228f83d75afc551bdf04bb0820962a459a2fe2b
MD5 b1721c4eaa58b56cece70c5601874d22
BLAKE2b-256 470eae033f59825298a7ccbd9c980fc6d563b8ed0e17952d7bf7af260e385357

See more details on using hashes here.

File details

Details for the file osbtlib-0.0.4-py3-none-any.whl.

File metadata

  • Download URL: osbtlib-0.0.4-py3-none-any.whl
  • Upload date:
  • Size: 13.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.9.18

File hashes

Hashes for osbtlib-0.0.4-py3-none-any.whl
Algorithm Hash digest
SHA256 0a2dedafd64617e43437c12ba221d956bf560cb613e8685efced3ad2a2f54bf7
MD5 15abb0bea67827304ab86fd91220f6ae
BLAKE2b-256 166ab8db635918f6eb0013f525e01dedc6316808359b933d2d6c8dfb9ae14e9c

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page