
Учимся создавать агентов для пентеста с использованием агента на React от LangGraph.
Моя цель — создать AI-агентов, которые помогут автоматизировать часть задач, выполняемых в рамках пентеста.
Для стартового проекта я решил создать агента, который умеет анализировать JavaScript файлы, находить скрытые API эндпоинты и проверять их на потенциальные уязвимости.
Выбор фреймворка
В качестве фреймворка я выбрал LangGraph, потому что я уже завершил LangGraph Academy и хорошо понял его. Он популярен, хорошо документирован и есть множество примеров его использования, поэтому начать лучше всего именно с него.
До этого мой опыт ограничивался простыми LLM-воркфлоу и связыванием нескольких вызовов. Я все еще не понимал как спроектировать AI-агента, который мог бы «думать» самостоятельно и использовать правильные инструменты.
Все изменилось, когда я наткнулся на статью Аншумана Бхартии, которая познакомила меня с React агентом в LangGraph. ReAct расшифровывается как Reasoning + Acting ( оригинальная статья ). Этот подход позволяет агенту использовать инструменты, анализировать результаты и циклически проходить шаги до достижения цели. Метод показался мне достаточно простым для реализации, и я решил построить на его основе свой проект.
Многое в моей работе основано на его наработках, за что я выражаю благодарность. В дальнейшем я планирую развивать эту концепцию самостоятельно.
Настройка уязвимого приложения
Чтобы протестировать своего агента, я создал уязвимое веб-приложение с помощью Python Flask. Внешне приложение выглядит простым, но его исходный код содержит JavaScript файл с несколькими скрытыми API эндпоинтами.
Каждый эндпоинт ведет себя по-разному: некоторые отвечают на базовые GET запросы, в то время как другие требуют кастомные заголовки, специфичные параметры или разные HTTP методы. Такая структура имитирует реальные API, где недокументированные или неправильно настроенные эндпоинты могут стать источником утечек данных.
from flask import Flask, request, jsonify
app = Flask(__name__)
# Vulnerable JavaScript file that will be served
VULNERABLE_JS = """
// API Configuration
const API_CONFIG = {
userInfo: '/api/v1/user-info', // Leaks sensitive data without auth
adminPanel: '/api/v1/admin', // Requires specific admin key
userProfile: '/api/v1/profile', // Requires X-User-Id header
};
// Admin key hardcoded (security vulnerability)
const ADMIN_KEY = 'super_secret_admin_key_123';
// Function to fetch user info (no auth required - vulnerability)
async function fetchUserInfo() {
const response = await fetch('/api/v1/user-info');
return response.json();
}
// Function to access admin panel
async function accessAdminPanel() {
const headers = {
'Content-Type': 'application/json',
'X-Admin-Key': ADMIN_KEY // Hardcoded admin key usage
};
const response = await fetch('/api/v1/admin', {
headers: headers
});
return response.json();
}
// Function to get user profile
async function getUserProfile(userId) {
const headers = {
'X-User-Id': userId // Required custom header
};
const response = await fetch('/api/v1/profile', {
headers: headers
});
return response.json();
}
"""
@app.route('/main.js')
def serve_js():
return VULNERABLE_JS, 200, {'Content-Type': 'application/javascript'}
@app.route('/api/v1/user-info')
def user_info():
# Vulnerable: Returns sensitive information without authentication
return jsonify({
"users": [
{"id": "1", "name": "John Doe", "ssn": "123-45-6789", "salary": 75000},
{"id": "2", "name": "Jane Smith", "ssn": "987-65-4321", "salary": 82000}
],
"database_connection": "mongodb://admin:password@localhost:27017",
"api_keys": {
"stripe": "sk_test_123456789",
"aws": "AKIA1234567890EXAMPLE"
}
})
@app.route('/api/v1/profile')
def user_profile():
# Requires X-User-Id header
user_id = request.headers.get('X-User-Id')
if not user_id:
return jsonify({"error": "X-User-Id header is required"}), 401
return jsonify({
"id": user_id,
"name": f"User {user_id}",
"email": f"@example.com">user{user_id}@example.com",
"role": "user"
})
@app.route('/api/v1/admin')
def admin_panel():
# Requires specific admin key value
admin_key = request.headers.get('X-Admin-Key')
if not admin_key:
return jsonify({"error": "X-Admin-Key header is required"}), 401
if admin_key != 'super_secret_admin_key_123': # Hardcoded key check
return jsonify({"error": "Invalid admin key"}), 403
return jsonify({
"sensitive_data": "This is sensitive admin data",
"internal_keys": {
"database": "root:password123",
"api_gateway": "private_key_xyz"
}
})
if name == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
Архитектура агента
Я начал с формулировки ключевых задач при создании своего агента:
1. Получить JavaScript файл
2. Найти в нём захардкоденные API эндпоинты
3. Выполнить запросы к обнаруженным эндпоинтам
4. Выявить наличие конфиденциальных данных в ответах
Для proof of concept я выбрал простой подход — передача JavaScript файла напрямую агенту с применением двух кастомных инструментов:
Endpoint Finder — regex-функция для поиска API путей в JavaScript коде
Sensitive Data Detector — инструмент, который делает запрос к эндпоинту и использует LLM для определения и классификации конфиденциальных данных в ответе
Подробнее об инструментах
Endpoint Finder
Простой regex-сканер для извлечения путей вида /api/... из JavaScript файлов. Хотя решение базовое, оно хорошо справляется с несжатым кодом. В перспективе я хочу проверить, сможет ли LLM «реверсить» сжатый JavaScript подобно человеку.
def find_endpoints_tool(url: str) -> list[str]:
"""
Find all API endpoints in the JavaScript code.
Args:
url (str): The URL of the JavaScript file to analyze.
"""
log_progress(f"Fetching JavaScript file from {url}")
resp = requests.get(url)
if resp.status_code == 200:
js = resp.text
patterns = [
r'["\']/(api/[^"\']*)["\']',
r':\s*["\']/(api/[^"\']*)["\']',
r'fetch\(["\']/(api/[^"\']*)["\']',
]
endpoints = []
for pattern in patterns:
matches = re.findall(pattern, js)
endpoints.extend(matches)
unique_endpoints = list(set(endpoints))
log_progress(f"Discovered endpoints: {unique_endpoints}")
return unique_endpoints
else:
raise Exception(f"Failed to fetch JavaScript file: {resp.status_code}")
Sensitive Data Detector
Этот инструмент выполняет HTTP запрос, а затем задействует GPT-4 для обнаружения конфиденциальной информации (SSN, API keys, email адреса и т.д.). Результат возвращается в JSON формате для удобства обработки.
Для этой модели я установил параметр temperature=0, что делает её выводы более определенными и предсказуемыми — хотя в будущем этот параметр потребует дополнительной настройки.
def sensitive_data_detection_tool(url: str):
"""
Analyze the HTTP response for sensitive data.
Args:
url (str): The URL of the HTTP response to analyze.
"""
log_progress(f"Fetching and analyzing data from {url}")
resp = requests.get(url)
if resp.status_code == 200:
model = ChatOpenAI(model="gpt-4", temperature=0)
prompt = f"""
You are a security researcher analyzing a web application for sensitive data vulnerabilities.
Your task is to identify any sensitive data in the HTTP response. Sensitive data includes, but not limited to:
- names
- email addresses
- phone numbers
- SSN
- credit card numbers
- API keys
ALWAYS return your response as a JSON object with the following structure (do NOT use markdown formatting):
``
{{
"data": "<sensitive data>",
"type": "<data type>"
}}
`
Example response:
`
{{
"data": "111-123-3201",
"type": "phone"
}}
`
HTTP Response:
`
{resp.text}
``
"""
return model.invoke(prompt)
Интеграция в LangGraph
Финальным шагом стала сборка агента с помощью функции create_react_agent:
1. Инициализация GPT-4 с детерминированными настройками (temperature=0)
2. Настройка system prompt с пошаговой инструкцией для агента
3. Регистрация кастомных инструментов
4. Передача пользовательского сообщения с задачей проанализировать JavaScript файл
def main():
sys_msg = SystemMessage(content="""
You are a security researcher analyzing a web application for potential vulnerabilities.
Your task is to identify new API endpoints to be analyzed for potential security weaknesses.
Follow these steps PRECISELY:
1. Use the find_endpoints tool to hidden discovery API endpoints from JS files. This will return a list of API endpoints
2. For each discovered API endpoint use the sensitive_data_detection tool to search for sensitive data.
3. Print out ONLY the sensitive data and the type of data found. Include which endpoint it was found at.
""")
model = ChatOpenAI(model="gpt-4", temperature=0)
model_sysmsg = model.bind(system_message=sys_msg)
agent = create_react_agent(
model_sysmsg,
tools=[find_endpoints_tool, sensitive_data_detection_tool],
)
msg = HumanMessage(content="Analyze the JS file at http://localhost:5000/main.js for API endpoints and search them for sensitive data.")
result = agent.invoke({
"messages": [msg],
"config": {"recursion_limit": 20}
})
# Print out system messages from the agent's output
for message in result.get("messages", []):
print("=" * 50)
print(message.content)
Запуск и тестирование
Запуск тестового сервера выглядит следующим образом:

После его старта я запустил свой агент:

В процессе работы агент:
Обнаружил три эндпоинта (/api/v1/user-info, /api/v1/profile, /api/v1/admin)
Корректно идентифицировал конфиденциальные данные (API keys, SSN, имена) в эндпоинте /api/v1/user-info
Два оставшихся эндпоинта были пропущены , поскольку они требовали дополнительных заголовков или ключей, которые агент пока не умеет обрабатывать.
Результат работы агента представлены в следующем фрагменте:
The analysis of the JavaScript file at http://localhost:5000/main.js revealed three API endpoints:
1. http://localhost:5000/api/v1/admin 2. http://localhost:5000/api/v1/user-info 3. http://localhost:5000/api/v1/profile
Upon further analysis for sensitive data, the following was found:
In the http://localhost:5000/api/v1/user-info endpoint, the following sensitive data was detected:
- API Key: AKIA1234567890EXAMPLE
- API Key: sk_test_123456789
- Name: John Doe
- SSN: 123-45-6789
- Name: Jane Smith
- SSN: 987-65-4321
No sensitive data was detected in the other two endpoints.
Лог сервера с отправкой HTTP запросов от агента выглядит следующим образом:

Итоги и выводы
Что получилось
Интуитивный интерфейс: React агент LangGraph оказался простым в использовании — я мог сосредоточиться на логике, а не на технических деталях
Рабочий прототип: Агент успешно выполнил базовые задачи по обнаружению эндпоинтов и идентификации конфиденциальных данных
Что требует доработки
· Неполный анализ: Агент пропустил захардкоженную строку подключения к БД в /api/v1/user-info. Вероятно, нужны более точные примеры в промптах или эксперименты с настройками параметров модели.
· Чувствительность к формулировкам: Изначально агент использовал только инструмент поиска эндпоинтов. Мне пришлось явно указать в промпте «search for sensitive data», несмотря на наличие этой инструкции в system prompt. Это показывает важность грамотного составления промптов
Планы по улучшению
Добавить поддержку эндпоинтов с требованиями к заголовкам, параметрам и HTTP методам через создание дополнительных инструментов
Изучить продвинутые техники ReAct промптов для повышения точности работы
Экспериментировать с разными подходами к prompt engineering, включая создание тестовых окружений для оценки эффективности различных промптов
Протестировать альтернативные LLM модели для поиска оптимального решения
Заключение
Несмотря на простоту реализации, прототип демонстрирует значительный потенциал автоматизации рутинных задач пентеста. В следующих итерациях я планирую улучшить интеллектуальные возможности агента — научить его распознавать требования авторизации, работать с разными HTTP методами и адаптироваться к реальным сложным сценариям.
Для всех, кому понравилась статья, исходный код агента для собственных тестов доступен по ссылке.
|
Хотите следить за новыми материалами и новостями из мира информационной безопасности? В Telegram‑канале AP Security Вы найдёте большое количество материалов по наступательной безопасности, защите корпоративной сети, компьютерной криминалистике, а также статьи, созданные командой лаборатории кибербезопасности компании AP Security. |
axelmaker
Спасибо за статью
Поправьте форматирование кода, очень тяжело читается
ap_security Автор
Спасибо за отзыв, рады, что статья оказалась полезной для Вас!
Текст также отредактировали для более удобного ознакомления.