Skip to content

Commit 1848143

Browse files
committed
Updates framework detectors for web2py
1 parent 26471d1 commit 1848143

File tree

2 files changed

+298
-1
lines changed

2 files changed

+298
-1
lines changed

siddhis/framewalk/detectors/web2py.py

Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
#!/usr/bin/env python3
2+
# __ _
3+
# \/imana 2016
4+
# [|-ramewørk
5+
#
6+
#
7+
# Author: s4dhu
8+
# Email: <s4dhul4bs[at]prontonmail[dot]ch
9+
# Git: @s4dhulabs
10+
# Mastodon: @s4dhu
11+
#
12+
# This file is part of Vimana Framework Project.
13+
14+
import re
15+
from typing import Dict, List, Any, Optional
16+
from urllib.parse import urljoin
17+
18+
from .base import BaseDetector
19+
20+
21+
class Web2pyDetector(BaseDetector):
22+
"""Web2py-specific detection methods"""
23+
24+
FRAMEWORK = "Web2py"
25+
26+
# Common Web2py paths to check
27+
COMMON_PATHS = [
28+
'/admin/',
29+
'/appadmin/',
30+
'/static/',
31+
'/welcome/',
32+
'/default/',
33+
'/_admin/',
34+
'/admin/default/',
35+
'/admin/default/index',
36+
'/admin/default/login',
37+
]
38+
39+
# Web2py error patterns
40+
ERROR_PATTERNS = [
41+
# Pattern, Description, Confidence
42+
(r'web2py_error:', 'Web2py error header', 15),
43+
(r'web2py_error: invalid application', 'Web2py invalid application error', 20),
44+
(r'web2py_error: ticket', 'Web2py ticket error', 15),
45+
(r'web2py_error: ticket invalid', 'Web2py invalid ticket error', 18),
46+
(r'web2py_error: application', 'Web2py application error', 12),
47+
]
48+
49+
# Web2py server patterns
50+
SERVER_PATTERNS = [
51+
# Pattern, Description, Confidence
52+
(r'Rocket\d+', 'Rocket server (Web2py default)', 25),
53+
(r'Rocket3', 'Rocket3 server (Web2py default)', 30),
54+
(r'web2py', 'Web2py server reference', 20),
55+
]
56+
57+
# Web2py session cookie patterns
58+
SESSION_PATTERNS = [
59+
# Pattern, Description, Confidence
60+
(r'session_id_\w+=', 'Web2py session cookie pattern', 25),
61+
(r'session_id_admin=', 'Web2py admin session cookie', 30),
62+
(r'session_id_welcome=', 'Web2py welcome app session cookie', 25),
63+
]
64+
65+
# Web2py content patterns
66+
CONTENT_PATTERNS = [
67+
# Pattern, Description, Confidence
68+
(r'web2py', 'Web2py reference in content', 5),
69+
(r'{{=', 'Web2py template syntax', 15),
70+
(r'{{extend', 'Web2py template extend', 20),
71+
(r'{{include', 'Web2py template include', 20),
72+
(r'{{block', 'Web2py template block', 20),
73+
(r'{{pass', 'Web2py template pass', 20),
74+
(r'{{if', 'Web2py template if', 15),
75+
(r'{{for', 'Web2py template for', 15),
76+
(r'{{try', 'Web2py template try', 15),
77+
(r'{{except', 'Web2py template except', 15),
78+
(r'{{finally', 'Web2py template finally', 15),
79+
(r'{{def', 'Web2py template def', 20),
80+
(r'{{return', 'Web2py template return', 15),
81+
]
82+
83+
# Web2py header patterns
84+
HEADER_PATTERNS = [
85+
# Header name, Pattern, Description, Confidence
86+
('X-Powered-By', r'web2py', 'X-Powered-By header contains web2py', 30),
87+
('Server', r'Rocket\d+', 'Server header contains Rocket', 25),
88+
('Server', r'web2py', 'Server header contains web2py', 20),
89+
]
90+
91+
def detect(self) -> None:
92+
"""Run Web2py detection methods"""
93+
self._check_headers()
94+
self._check_common_paths()
95+
self._check_error_patterns()
96+
self._check_server_patterns()
97+
self._check_session_patterns()
98+
self._check_content_patterns()
99+
self._check_admin_interface()
100+
self.detect_version()
101+
102+
def _add_score(self,
103+
points: int,
104+
evidence_type: str,
105+
detail: str,
106+
raw_data: Optional[Dict[str, Any]] = None) -> None:
107+
"""Add score for Web2py"""
108+
self.result_manager.add_score(self.FRAMEWORK, points, evidence_type, detail, raw_data)
109+
110+
def _add_version_hint(self,
111+
version: str,
112+
confidence: int,
113+
evidence: str) -> None:
114+
"""Add version hint for Web2py"""
115+
self.result_manager.add_version_hint(self.FRAMEWORK, version, confidence, evidence)
116+
117+
def _add_component(self,
118+
component: str,
119+
evidence: str) -> None:
120+
"""Add component for Web2py"""
121+
self.result_manager.add_component(self.FRAMEWORK, component, evidence)
122+
123+
def _check_headers(self) -> None:
124+
"""Check for Web2py-specific headers"""
125+
response = self.request_manager.make_request()
126+
if not response:
127+
return
128+
129+
headers = response.headers
130+
131+
# Check header patterns
132+
for header_name, pattern, description, confidence in self.HEADER_PATTERNS:
133+
if header_name in headers:
134+
header_value = headers[header_name]
135+
if re.search(pattern, header_value, re.IGNORECASE):
136+
self._add_score(
137+
confidence,
138+
'Header',
139+
f"{description}: {header_name}: {header_value}"
140+
)
141+
142+
# Check for web2py in any header
143+
for name, value in headers.items():
144+
if 'web2py' in value.lower():
145+
self._add_score(
146+
15,
147+
'Header',
148+
f"{name} header contains web2py: {value}"
149+
)
150+
151+
def _check_common_paths(self) -> None:
152+
"""Check for Web2py-specific paths"""
153+
base_url = self.request_manager.target_url.rstrip('/')
154+
155+
for path in self.COMMON_PATHS:
156+
url = urljoin(base_url, path)
157+
response = self.request_manager.make_request(url)
158+
159+
if response:
160+
# Check for web2py error patterns in response
161+
if 'web2py_error:' in response.text:
162+
self._add_score(
163+
20,
164+
'Endpoint',
165+
f"{path} returns web2py error response"
166+
)
167+
168+
# Check for successful admin access
169+
if path == '/admin/' and response.status_code == 200:
170+
self._add_score(
171+
25,
172+
'Endpoint',
173+
f"{path} returns 200 OK (Web2py admin interface)"
174+
)
175+
176+
# Check for web2py session cookies
177+
if 'session_id_' in response.headers.get('Set-Cookie', ''):
178+
self._add_score(
179+
20,
180+
'Endpoint',
181+
f"{path} sets web2py session cookie"
182+
)
183+
184+
def _check_error_patterns(self) -> None:
185+
"""Check for Web2py error patterns"""
186+
response = self.request_manager.make_request()
187+
if not response:
188+
return
189+
190+
# Check error patterns in response text
191+
for pattern, description, confidence in self.ERROR_PATTERNS:
192+
if re.search(pattern, response.text, re.IGNORECASE):
193+
self._add_score(
194+
confidence,
195+
'Error',
196+
f"{description}: {pattern}"
197+
)
198+
199+
# Check error patterns in headers
200+
for name, value in response.headers.items():
201+
for pattern, description, confidence in self.ERROR_PATTERNS:
202+
if re.search(pattern, value, re.IGNORECASE):
203+
self._add_score(
204+
confidence,
205+
'Error',
206+
f"{description} in {name} header: {value}"
207+
)
208+
209+
def _check_server_patterns(self) -> None:
210+
"""Check for Web2py server patterns"""
211+
response = self.request_manager.make_request()
212+
if not response:
213+
return
214+
215+
server_header = response.headers.get('Server', '')
216+
217+
for pattern, description, confidence in self.SERVER_PATTERNS:
218+
if re.search(pattern, server_header, re.IGNORECASE):
219+
self._add_score(
220+
confidence,
221+
'Server',
222+
f"{description}: {server_header}"
223+
)
224+
225+
def _check_session_patterns(self) -> None:
226+
"""Check for Web2py session patterns"""
227+
response = self.request_manager.make_request()
228+
if not response:
229+
return
230+
231+
# Check Set-Cookie header
232+
set_cookie = response.headers.get('Set-Cookie', '')
233+
234+
for pattern, description, confidence in self.SESSION_PATTERNS:
235+
if re.search(pattern, set_cookie, re.IGNORECASE):
236+
self._add_score(
237+
confidence,
238+
'Session',
239+
f"{description}: {set_cookie}"
240+
)
241+
242+
def _check_content_patterns(self) -> None:
243+
"""Check for Web2py content patterns"""
244+
response = self.request_manager.make_request()
245+
if not response:
246+
return
247+
248+
# Check content patterns
249+
for pattern, description, confidence in self.CONTENT_PATTERNS:
250+
if re.search(pattern, response.text, re.IGNORECASE):
251+
self._add_score(
252+
confidence,
253+
'Content',
254+
f"{description}: {pattern}"
255+
)
256+
257+
def _check_admin_interface(self) -> None:
258+
"""Check for Web2py admin interface"""
259+
base_url = self.request_manager.target_url.rstrip('/')
260+
admin_url = urljoin(base_url, '/admin/')
261+
262+
response = self.request_manager.make_request(admin_url)
263+
if response and response.status_code == 200:
264+
# Check for web2py admin characteristics
265+
if 'session_id_admin=' in response.headers.get('Set-Cookie', ''):
266+
self._add_score(
267+
30,
268+
'Admin',
269+
"Web2py admin interface detected with admin session cookie"
270+
)
271+
self._add_component(
272+
"Admin Interface",
273+
"Web2py admin interface accessible at /admin/"
274+
)
275+
276+
def detect_version(self) -> None:
277+
"""Attempt to detect Web2py version"""
278+
response = self.request_manager.make_request()
279+
if not response:
280+
return
281+
282+
# Check server header for Rocket version
283+
server_header = response.headers.get('Server', '')
284+
rocket_match = re.search(r'Rocket(\d+\.\d+\.\d+)', server_header)
285+
if rocket_match:
286+
rocket_version = rocket_match.group(1)
287+
self._add_version_hint(
288+
f"Rocket {rocket_version}",
289+
70,
290+
f"Rocket server version detected: {rocket_version}"
291+
)
292+
293+
# Check for web2py version in headers or content
294+
# This would require more specific version detection logic
295+
# based on web2py's versioning scheme

siddhis/framewalk/orchestrator/fwalk_orchestrator.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ def _init_detectors(self) -> None:
242242
from ..detectors.fastapi import FastAPIDetector
243243
from ..detectors.pyramid import PyramidDetector
244244
from ..detectors.bottle import BottleDetector
245+
from ..detectors.web2py import Web2pyDetector
245246

246247
# Clear any existing detectors
247248
self.detectors = []
@@ -260,7 +261,8 @@ def _init_detectors(self) -> None:
260261
'flask': FlaskDetector,
261262
'fastapi': FastAPIDetector,
262263
'pyramid': PyramidDetector,
263-
'bottle': BottleDetector
264+
'bottle': BottleDetector,
265+
'web2py': Web2pyDetector
264266
}
265267

266268
for framework_name, detector_class in detector_classes.items():

0 commit comments

Comments
 (0)