Skip to content

Sign-python-scripter #18

@GpKos

Description

@GpKos

Механизм безопасности API-запросов.

i

Этот механизм использует ключи RSA для проверки целостности запросов.

Формируется новый заголовок XX-Signature, который вставляется в запрос.

Что требуется:

  • Приватный ключ - хранится в секрете у клиента и используется для создания цифровой подписи каждого отправляемого API-запроса.
  • Ключ для проверки - сервер подписывает уведомления своим приватным ключом, а торговец проверяет их публичным ключом.
  • Python Scripter (Burp Suite).
  • Алгоритм формирования подписи

2025-04-13_18-13-13

Решение

Burp Suite работает с Jython. Jython - это реализация языка Python, которая работает на платформе Java Virtual Machine (JVM). Поскольку Jython взаимодействует с Java-кодом и использует Java-библиотеки, нужно использовать системные вызовы через библиотеку subprocess:
123

Иллюстрация работы плагина и скрипта.

Ниже представлен код Python, реализующий описанный механизм подписи запроса:

2025-04-14_10-56-35

На рисунке 1 проиллюстрирована попытка отправки запроса без дополнительного заголовка. Как можно заметить, сервер указал на его отсутствие и не обработал сообщение.

1

На рисунке 2 изображена еще одна попытка отправить запрос, на этот раз с добавлением требуемого заголовка. Однако изменение в теле сообщения вызвало отсутствие совместимости с подписью, в результате чего сервер также отклонил сообщение.

2

На рисунке 3 представлен измененный запрос, зарегистрированный модулем Burp Logger, который был отправлен на сервер.

3

Дополнительный материал:

import base64
import subprocess
try:
    import urllib.parse as urlparse  # Try python 3 first
except ImportError:
    import urllib as urlparse  # Fallback for python 2

PRIVATE_KEY = "/path/to/key"
SIGNATURE_HEADER = 'XX-Signature'

if messageIsRequest:
    print("Executing signature code")

    requestInfo = helpers.analyzeRequest(messageInfo.getRequest())
    headers = requestInfo.getHeaders()
    requestBody = messageInfo.getRequest()[requestInfo.getBodyOffset():]
    url = messageInfo.getUrl()
    method = requestInfo.getMethod().upper()
    path = url.getPath()

    #  Удалено URL-кодирование параметров
    query_string = url.getQuery()
    if query_string:
        path += "?" + query_string

    msg = helpers.bytesToString(requestBody)

    # Construct signature input string with UTF-8 encoding and explicit newline.
    signature_input = "{}\n{}\n{}".format(method, path, msg)

    print('signature_input', signature_input)

    # Use openssl via subprocess.  Correctly handle encoding and decoding.
    try:
        cmd = "openssl dgst -sha256 -sign {}".format(PRIVATE_KEY)  # Use .format() instead of f-string
        process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
        output, err = process.communicate(input=signature_input.encode('utf-8'))  # Encode input for openssl

        if err:
            raise Exception(err.decode('utf-8'))

        signature_bytes = base64.b64encode(output).decode('utf-8').strip()  # Base64 encode the result

        signature = signature_bytes
    except Exception as e:
        print("Error creating signature: {}".format(e)) # Use .format() instead of f-string
        signature = ""  # or handle the error in another way, e.g., return None

    new_sign = '{}: {}'.format(SIGNATURE_HEADER, signature)
    print('Adding new', new_sign)

    newHeaders = []
    print("Original headers:", headers)
    # Remove existing XX-Signature headers
    for h in headers:
        if SIGNATURE_HEADER not in h:
            newHeaders.append(h)
        else:
            print('Header exist, removing: ', h)
    print("Headers after removing existing signatures:", newHeaders)

    print("New signature header:", new_sign)

    # Insert the new XX-Signature header as the *second* header in the list.
    if len(newHeaders) > 0:
        newHeaders.insert(1, new_sign)
    else:
        newHeaders.append(new_sign)  # if there are no headers, put as first

    print("Final headers:", newHeaders)
    request = helpers.buildHttpMessage(newHeaders, requestBody)

    messageInfo.setRequest(request)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions