Skip to content

Commit a7db1f8

Browse files
committed
misc addons
2 parents ed31cc4 + 1e81817 commit a7db1f8

File tree

5 files changed

+181
-39
lines changed

5 files changed

+181
-39
lines changed

.gitignore

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,106 @@
1+
# Byte-compiled / optimized / DLL files
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
# C extensions
6+
*.so
17
*~
8+
# Distribution / packaging
9+
.Python
10+
env/
11+
build/
12+
develop-eggs/
13+
dist/
14+
downloads/
15+
eggs/
16+
.eggs/
17+
lib/
18+
lib64/
19+
parts/
20+
sdist/
21+
var/
22+
wheels/
23+
*.egg-info/
24+
.installed.cfg
25+
*.egg
26+
27+
# PyInstaller
28+
# Usually these files are written by a python script from a template
29+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
30+
*.manifest
31+
*.spec
32+
33+
# Installer logs
34+
pip-log.txt
35+
pip-delete-this-directory.txt
36+
37+
# Unit test / coverage reports
38+
htmlcov/
39+
.tox/
40+
.coverage
41+
.coverage.*
42+
.cache
43+
nosetests.xml
44+
coverage.xml
45+
*.cover
46+
.hypothesis/
47+
48+
# Translations
49+
*.mo
50+
*.pot
51+
52+
# Django stuff:
53+
*.log
54+
local_settings.py
55+
56+
# Flask stuff:
57+
instance/
58+
.webassets-cache
59+
60+
# Scrapy stuff:
61+
.scrapy
62+
63+
# Sphinx documentation
64+
docs/_build/
65+
66+
# PyBuilder
67+
target/
68+
69+
# Jupyter Notebook
70+
.ipynb_checkpoints
71+
72+
# pyenv
73+
.python-version
74+
75+
# celery beat schedule file
76+
celerybeat-schedule
77+
78+
# SageMath parsed files
79+
*.sage.py
80+
81+
# dotenv
82+
.env
83+
84+
# virtualenv
85+
.venv
86+
venv/
87+
ENV/
88+
89+
# Spyder project settings
90+
.spyderproject
91+
.spyproject
92+
93+
# Rope project settings
94+
.ropeproject
95+
96+
# mkdocs documentation
97+
/site
98+
99+
# mypy
100+
.mypy_cache/
101+
102+
# Vim
103+
*.swn
104+
*.swm
105+
*.swo
106+
*.swp

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@ See https://isc.sans.edu/diary.html?storyid=22900
55

66
This is a simple (too simple?) Python script that will read a pcap, find HTTP requests and turn them into cURL commands for replay.
77

8-
Little effort is made to verify that the requests are valid. This is intended to extract well formed requests that were created by your browser. Not necessarily intedned for malicious requests. It also does not reassemble TCP streams (yet). Browsers typically send requests as one packet, but large requests will fail.
8+
Little effort is made to verify that the requests are valid. This is intended to extract well formed requests that were created by your browser. Not necessarily intended for malicious requests. It also does not reassemble TCP streams (yet). Browsers typically send requests as one packet, but large requests will fail.
99

10-
DISCLAIMER: I am not a Python coder. I do not like Python. I have to use it once in a while because I love Scapy.
10+
DISCLAIMER: I am not a Python coder. I do not like Python. I have to use it once in a while because I love [Scapy](http://www.secdev.org/projects/scapy/).
1111

1212
CREDIT: Stackoverflow
13-

TODO

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
- session reassembly
22
- make the matches for things like "Host" more forgiving, at least case insensitive
33
- allow for HTTP traffic not on port 80
4-
- only allow valid "Methods"
54
- better URL validation
65
- Escape single quotes

pcap2curl.py

Lines changed: 56 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,64 @@
11
#!/usr/bin/env python
22

33
import sys
4-
from scapy.all import *
4+
from scapy.all import PcapReader, re, Raw, TCP
55

66

7-
if len(sys.argv) != 2:
8-
print ("I need an input file. Usage ./pcap2curl.py inputfilename")
9-
exit()
7+
VALID_METHODS = [
8+
"GET",
9+
"HEAD",
10+
"POST",
11+
"PUT",
12+
"DELETE",
13+
"CONNECT",
14+
"OPTIONS",
15+
"TRACE",
16+
"PATCH"
17+
] # see https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
1018

11-
infile = sys.argv[1]
12-
13-
packets=rdpcap(infile)
1419

1520
def payload2curl(p):
16-
lines=re.compile("[\n\r]+").split(p)
17-
startline=re.search('^([A-Z]+) ([^ ]+) (HTTP\/[0-9\/]+)',lines[0])
18-
curl='curl ';
19-
method=startline.group(1)
20-
url=startline.group(2)
21-
version=startline.group(3)
22-
23-
del lines[0]
24-
headers=[]
25-
for line in lines:
26-
if ":" in line:
27-
headers.append("-H '"+line+"'")
28-
if "Host:" in line:
29-
hostheader=re.search("^Host: (.*)",line)
30-
hostname=hostheader.group(1)
31-
32-
if hostname not in url:
33-
url='http://'+hostname+'/'+url
34-
curl=curl+' '+"'"+url+"' \\\n"
35-
curl=curl+'-X'+method+" \\\n"
36-
curl=curl+" \\\n".join(headers)
37-
return curl
38-
39-
for p in packets:
40-
payload=''
41-
if p.haslayer(TCP) and p.haslayer(Raw) and p[TCP].dport == 80:
42-
payload=p[Raw].load
43-
print (payload2curl(payload))
21+
lines = re.compile("[\n\r]+").split(p.decode())
22+
start_line = re.search("^([A-Z]+) ([^ ]+) (HTTP\/[0-9\/]+)", lines[0])
23+
method = start_line.group(1)
24+
url = start_line.group(2)
25+
version = start_line.group(3) # Never used
26+
27+
if method not in VALID_METHODS:
28+
return
29+
30+
del lines[0]
31+
headers = []
32+
for line in lines:
33+
if ":" in line:
34+
headers.append("-H '{}'".format(line))
35+
if "Host:" in line:
36+
host_header = re.search("^Host: (.*)", line)
37+
host_name = host_header.group(1)
38+
39+
proto_host = 'http://{}/'.format(host_name)
40+
if not url.startswith(proto_host):
41+
url = "{}{}".format(proto_host, url[1:] if url[0] == "/" else url)
42+
curl = "curl '{}' \\\n -X {} \\\n ".format(url, method)
43+
curl += " \\\n ".join(headers)
44+
return curl
45+
46+
47+
def main():
48+
if len(sys.argv) != 2:
49+
print ("I need an input file. Usage ./pcap2curl.py inputfilename")
50+
return
51+
52+
infile = sys.argv[1]
53+
54+
with PcapReader(infile) as packets:
55+
for p in packets:
56+
if p.haslayer(TCP) and p.haslayer(Raw) and p[TCP].dport == 80:
57+
payload = p[Raw].load
58+
cmd = payload2curl(payload)
59+
if cmd:
60+
print(cmd)
61+
62+
63+
if __name__ == "__main__":
64+
main()

setup.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from setuptools import setup
2+
3+
setup(
4+
name='pcap2curl',
5+
6+
version='0.1',
7+
8+
description='Extract HTTP requests from pcap and turn them into cURL',
9+
10+
url='https://github.com/jullrich/pcap2curl',
11+
12+
author='Johannes Ullrich',
13+
14+
license='GNU',
15+
16+
install_requires=['scapy']
17+
18+
)

0 commit comments

Comments
 (0)