Compare commits

...

60 Commits

Author SHA1 Message Date
codex-action
6251c5e1fd Fix CI failures for PR #574 2026-01-20 22:31:08 +00:00
Carlos Polop
92d9f27e8b Test CI failure flow 2026-01-20 23:24:03 +01:00
Carlos Polop
b24694f00b Gate codex triage on successful PR tests 2026-01-20 23:23:42 +01:00
Carlos Polop
e777c81eba Run Codex triage after PR tests succeed 2026-01-20 23:23:42 +01:00
SirBroccoli
21a86bc365 Auto-merge PR #573 (Codex) 2026-01-20 22:17:38 +00:00
SirBroccoli
ac7cb9c73c Auto-merge PR #572 (Codex) 2026-01-20 22:17:02 +00:00
SirBroccoli
d054715fbd Merge pull request #564 from peass-ng/fix/issue-410-printnightmare
Check PrintNightmare PointAndPrint policy
2026-01-20 23:06:58 +01:00
SirBroccoli
b4c1043a93 Merge branch 'master' into fix/issue-410-printnightmare 2026-01-20 23:06:29 +01:00
Carlos Polop
1b8706aac6 Handle missing PointAndPrint values in PS1 2026-01-20 23:04:35 +01:00
SirBroccoli
3371be7bd6 Merge pull request #557 from peass-ng/fix/issue-474-service-timeout
Add timeout to service enumeration
2026-01-20 23:02:35 +01:00
SirBroccoli
2344f5b106 Auto-merge PR #570 (Codex) 2026-01-20 17:25:25 +00:00
SirBroccoli
485f91d46c Auto-merge PR #569 (Codex) 2026-01-20 17:25:00 +00:00
SirBroccoli
018e8866e6 Auto-merge PR #568 (Codex) 2026-01-20 17:24:04 +00:00
SirBroccoli
d707317278 Auto-merge PR #567 (Codex) 2026-01-20 17:23:33 +00:00
SirBroccoli
f4ef371afc Auto-merge PR #566 (Codex) 2026-01-20 17:22:47 +00:00
SirBroccoli
61f6282b5f Auto-merge PR #565 (Codex) 2026-01-20 17:22:35 +00:00
codex-action
a363541d77 Fix CI failures for PR #564 2026-01-20 17:09:07 +00:00
Carlos Polop
6fc41c9a23 Add PrintNightmare PointAndPrint policy check 2026-01-20 18:03:55 +01:00
SirBroccoli
170a4b2c70 Auto-merge PR #563 (Codex) 2026-01-20 17:03:45 +00:00
codex-action
710709834a Fix CI failures for PR #557 2026-01-20 17:03:40 +00:00
SirBroccoli
21b2bac892 Auto-merge PR #562 (Codex) 2026-01-20 17:03:14 +00:00
SirBroccoli
5fdb99b38e Auto-merge PR #561 (Codex) 2026-01-20 17:02:17 +00:00
SirBroccoli
787bc8fa8a Auto-merge PR #560 (Codex) 2026-01-20 17:01:24 +00:00
SirBroccoli
c5401bd33d Auto-merge PR #559 (Codex) 2026-01-20 17:00:50 +00:00
SirBroccoli
bd18d96837 Auto-merge PR #558 (Codex) 2026-01-20 17:00:10 +00:00
SirBroccoli
ede5960b7c Auto-merge PR #556 (Codex) 2026-01-20 16:59:18 +00:00
Carlos Polop
c54f483648 Add timeout to service enumeration in extra checks 2026-01-20 17:58:36 +01:00
SirBroccoli
e533bf3ba5 Auto-merge PR #555 (Codex) 2026-01-20 16:58:29 +00:00
SirBroccoli
66c3d4e342 Merge pull request #554 from peass-ng/fix-pr-failure-dispatch-context-2
Fix pr failure dispatch context 2
2026-01-20 17:46:27 +01:00
Carlos Polop
917f88b76c f 2026-01-20 17:45:55 +01:00
Carlos Polop
21a967acb5 fix urls 2026-01-20 13:59:32 +01:00
Carlos Polop
4155093e56 fix 2026-01-19 13:28:51 +01:00
Carlos Polop
be1b0cdbd0 f 2026-01-19 01:27:47 +01:00
SirBroccoli
89a55bde9b Auto-merge PR #553 (Codex) 2026-01-18 23:11:38 +00:00
Carlos Polop
4308caddf1 Fix PR context parsing in failure dispatch 2026-01-19 00:10:42 +01:00
SirBroccoli
54fc62d29b Auto-merge PR #552 (Codex) 2026-01-18 23:06:21 +00:00
SirBroccoli
9216b31b10 Auto-merge PR #550 (Codex)
* f

* new actions

* Doc tweak for Codex merge test
2026-01-18 22:59:38 +00:00
SirBroccoli
9d8a14d2ec Merge pull request #545 from peass-ng/update_PEASS-linpeas-ECS_on_EC2__Covering_Gaps_in_IMDS_Ha_20251229_015718
[LINPEAS] Add privilege escalation check: ECS on EC2 Covering Gaps in IMDS Hardeni...
2026-01-17 16:25:39 +01:00
SirBroccoli
9c49dfd2bb Merge pull request #529 from peass-ng/update_PEASS-winpeas-Pwning_ASUS_DriverHub__MSI_Center__A_20251207_130236
[WINPEAS] Add privilege escalation check: Pwning ASUS DriverHub, MSI Center, Acer ...
2026-01-17 16:06:06 +01:00
SirBroccoli
9acc2ef61d Merge pull request #540 from peass-ng/update_PEASS-winpeas-The_Windows_Registry_Adventure__Part_20251217_014635
[WINPEAS] Add privilege escalation check: The Windows Registry Adventure, Part 8 E...
2026-01-17 16:05:46 +01:00
Carlos Polop
e7663381f2 Merge master into PR 529 and resolve ServicesInfo conflict 2026-01-17 15:52:44 +01:00
Carlos Polop
ce5bd84575 Merge ECS IMDS checks into ECS module 2026-01-17 15:48:55 +01:00
SirBroccoli
c447ca993d Merge branch 'master' into update_PEASS-winpeas-The_Windows_Registry_Adventure__Part_20251217_014635 2026-01-17 15:45:17 +01:00
SirBroccoli
a1aa60faf8 Merge pull request #544 from peass-ng/update_PEASS-winpeas-Kerberoasting__Low-Tech__High-Impact_20251229_013424
[WINPEAS] Add privilege escalation check: Kerberoasting Low-Tech, High-Impact Atta...
2026-01-17 15:42:18 +01:00
SirBroccoli
e81c436d80 Merge branch 'master' into update_PEASS-winpeas-Kerberoasting__Low-Tech__High-Impact_20251229_013424 2026-01-17 15:42:10 +01:00
Carlos Polop
2f44379713 Fix registry hive resolution in ACL scanner 2026-01-17 15:33:09 +01:00
Carlos Polop
0ed7a39a7d Fix unassigned out vars in OEM pipe check 2026-01-17 15:21:50 +01:00
Carlos Polop
1cdd473d79 Merge branch 'master' into update_PEASS-winpeas-Pwning_ASUS_DriverHub__MSI_Center__A_20251207_130236 2026-01-17 13:36:49 +01:00
Carlos Polop
c14f9aeb30 Merge branch 'master' into update_PEASS-winpeas-The_Windows_Registry_Adventure__Part_20251217_014635 2026-01-17 13:36:22 +01:00
Carlos Polop
14aa117a0e Merge branch 'master' into update_PEASS-winpeas-Kerberoasting__Low-Tech__High-Impact_20251229_013424 2026-01-17 13:36:06 +01:00
Carlos Polop
7016e5a0b4 Merge branch 'master' into update_PEASS-linpeas-ECS_on_EC2__Covering_Gaps_in_IMDS_Ha_20251229_015718 2026-01-17 13:36:02 +01:00
Carlos Polop
7a5aa8dcae chore: trigger CI 2026-01-16 18:17:40 +01:00
Carlos Polop
aca58f36b9 chore: trigger CI 2026-01-16 18:17:14 +01:00
Carlos Polop
ff41d5b0e7 chore: trigger CI 2026-01-16 18:16:58 +01:00
Carlos Polop
fa58c6688b chore: trigger CI 2026-01-16 18:16:54 +01:00
Carlos Polop
3aa04a53fc chore: trigger CI 2026-01-16 18:15:20 +01:00
HackTricks News Bot
e77867b2d3 Add linpeas privilege escalation checks from: ECS on EC2: Covering Gaps in IMDS Hardening 2025-12-29 02:02:46 +00:00
HackTricks News Bot
be72fecfa8 Add winpeas privilege escalation checks from: Kerberoasting: Low-Tech, High-Impact Attacks from Legacy Kerberos Crypto 2025-12-29 01:42:21 +00:00
HackTricks News Bot
3268701ed6 Add winpeas privilege escalation checks from: The Windows Registry Adventure, Part 8: Exploitation of Hive-based Memory Corrup 2025-12-17 02:00:18 +00:00
HackTricks News Bot
6c75f10fae Add winpeas privilege escalation checks from: Pwning ASUS DriverHub, MSI Center, Acer Control Centre and Razer Synapse 4 2025-12-07 13:22:49 +00:00
44 changed files with 2138 additions and 86 deletions

18
.github/codex/pr-merge-schema.json vendored Normal file
View File

@@ -0,0 +1,18 @@
{
"type": "object",
"additionalProperties": false,
"properties": {
"decision": {
"type": "string",
"enum": ["merge", "comment"]
},
"message": {
"type": "string"
},
"confidence": {
"type": "string",
"enum": ["low", "medium", "high"]
}
},
"required": ["decision", "message", "confidence"]
}

View File

@@ -1,26 +0,0 @@
name: CI-PR_from_dev
on:
push:
branches:
- winpeas_dev
- linpeas_dev
workflow_dispatch:
jobs:
create_pull_request:
runs-on: ubuntu-latest
steps:
# checkout
- name: Checkout
uses: actions/checkout@v2
# PR
- name: Pull Request
uses: repo-sync/pull-request@v2
with:
destination_branch: "master"
github_token: ${{ secrets.PULL_REQUEST_TOKEN }}

167
.github/workflows/codex-pr-triage.yml vendored Normal file
View File

@@ -0,0 +1,167 @@
name: Codex PR Triage
on:
workflow_run:
workflows: ["PR-tests"]
types: [completed]
jobs:
codex_triage:
if: ${{ github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
outputs:
should_run: ${{ steps.gate.outputs.should_run }}
pr_number: ${{ steps.gate.outputs.pr_number }}
pr_title: ${{ steps.gate.outputs.pr_title }}
pr_body: ${{ steps.gate.outputs.pr_body }}
base_ref: ${{ steps.gate.outputs.base_ref }}
head_ref: ${{ steps.gate.outputs.head_ref }}
base_sha: ${{ steps.gate.outputs.base_sha }}
head_sha: ${{ steps.gate.outputs.head_sha }}
decision: ${{ steps.parse.outputs.decision }}
message: ${{ steps.parse.outputs.message }}
steps:
- name: Resolve PR context
id: gate
env:
GH_TOKEN: ${{ github.token }}
run: |
pr_number="${{ github.event.workflow_run.pull_requests[0].number }}"
if [ -z "$pr_number" ]; then
echo "No pull request found for this workflow_run; skipping."
echo "should_run=false" >> "$GITHUB_OUTPUT"
echo "pr_number=" >> "$GITHUB_OUTPUT"
exit 0
fi
author="$(gh pr view "$pr_number" --json author --jq .author.login)"
if [ "$author" != "carlospolop" ]; then
echo "PR author is $author; skipping."
echo "should_run=false" >> "$GITHUB_OUTPUT"
echo "pr_number=$pr_number" >> "$GITHUB_OUTPUT"
exit 0
fi
pr_title="$(gh pr view "$pr_number" --json title --jq .title)"
pr_body="$(gh pr view "$pr_number" --json body --jq .body)"
base_ref="$(gh pr view "$pr_number" --json baseRefName --jq .baseRefName)"
head_ref="$(gh pr view "$pr_number" --json headRefName --jq .headRefName)"
base_sha="$(gh pr view "$pr_number" --json baseRefOid --jq .baseRefOid)"
head_sha="$(gh pr view "$pr_number" --json headRefOid --jq .headRefOid)"
echo "should_run=true" >> "$GITHUB_OUTPUT"
echo "pr_number=$pr_number" >> "$GITHUB_OUTPUT"
echo "pr_title<<EOF" >> "$GITHUB_OUTPUT"
echo "$pr_title" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
echo "pr_body<<EOF" >> "$GITHUB_OUTPUT"
echo "$pr_body" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
echo "base_ref=$base_ref" >> "$GITHUB_OUTPUT"
echo "head_ref=$head_ref" >> "$GITHUB_OUTPUT"
echo "base_sha=$base_sha" >> "$GITHUB_OUTPUT"
echo "head_sha=$head_sha" >> "$GITHUB_OUTPUT"
- name: Checkout PR merge ref
uses: actions/checkout@v5
with:
ref: refs/pull/${{ steps.gate.outputs.pr_number }}/merge
if: ${{ steps.gate.outputs.should_run == 'true' }}
- name: Pre-fetch base and head refs
if: ${{ steps.gate.outputs.should_run == 'true' }}
run: |
git fetch --no-tags origin \
${{ steps.gate.outputs.base_ref }} \
+refs/pull/${{ steps.gate.outputs.pr_number }}/head
- name: Run Codex
id: run_codex
if: ${{ steps.gate.outputs.should_run == 'true' }}
uses: openai/codex-action@v1
with:
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
output-schema-file: .github/codex/pr-merge-schema.json
model: gpt-5.2-codex
prompt: |
You are reviewing PR #${{ steps.gate.outputs.pr_number }} for ${{ github.repository }}.
Decide whether to merge or comment. Merge only if all of the following are true:
- Changes are simple and safe (no DoS, no long operations, no backdoors).
- Changes follow common PEASS syntax and style without breaking anything and add useful checks or value.
- Changes simplify code or add new useful checks without breaking anything.
If you don't have any doubts, and all the previous conditions are met, decide to merge.
If you have serious doubts, choose "comment" and include your doubts or questions.
If you decide to merge, include a short rationale.
Pull request title and body:
----
${{ steps.gate.outputs.pr_title }}
${{ steps.gate.outputs.pr_body }}
Review ONLY the changes introduced by the PR:
git log --oneline ${{ steps.gate.outputs.base_sha }}...${{ steps.gate.outputs.head_sha }}
Output JSON only, following the provided schema.
- name: Parse Codex decision
id: parse
if: ${{ steps.gate.outputs.should_run == 'true' }}
env:
CODEX_MESSAGE: ${{ steps.run_codex.outputs.final-message }}
run: |
python3 - <<'PY'
import json
import os
data = json.loads(os.environ.get('CODEX_MESSAGE', '') or '{}')
decision = data.get('decision', 'comment')
message = data.get('message', '').strip() or 'Codex did not provide details.'
with open(os.environ['GITHUB_OUTPUT'], 'a') as handle:
handle.write(f"decision={decision}\n")
handle.write("message<<EOF\n")
handle.write(message + "\n")
handle.write("EOF\n")
PY
merge_or_comment:
runs-on: ubuntu-latest
needs: codex_triage
if: ${{ github.event.workflow_run.conclusion == 'success' && needs.codex_triage.outputs.should_run == 'true' && needs.codex_triage.outputs.decision != '' }}
permissions:
contents: write
pull-requests: write
steps:
- name: Merge PR when approved
if: ${{ needs.codex_triage.outputs.decision == 'merge' }}
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ needs.codex_triage.outputs.pr_number }}
run: |
gh api \
-X PUT \
-H "Accept: application/vnd.github+json" \
/repos/${{ github.repository }}/pulls/${PR_NUMBER}/merge \
-f merge_method=squash \
-f commit_title="Auto-merge PR #${PR_NUMBER} (Codex)"
- name: Comment with doubts
if: ${{ needs.codex_triage.outputs.decision == 'comment' }}
uses: actions/github-script@v7
env:
PR_NUMBER: ${{ needs.codex_triage.outputs.pr_number }}
CODEX_MESSAGE: ${{ needs.codex_triage.outputs.message }}
with:
github-token: ${{ github.token }}
script: |
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: Number(process.env.PR_NUMBER),
body: process.env.CODEX_MESSAGE,
});

View File

@@ -0,0 +1,167 @@
name: PR Failure Codex Dispatch
on:
workflow_run:
workflows: ["PR-tests"]
types: [completed]
jobs:
codex_on_failure:
if: >
${{ github.event.workflow_run.conclusion == 'failure' &&
github.event.workflow_run.pull_requests &&
github.event.workflow_run.pull_requests[0] &&
!startsWith(github.event.workflow_run.head_commit.message, 'Fix CI failures for PR #') }}
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
actions: read
steps:
- name: Resolve PR context
id: pr_context
env:
PR_NUMBER: ${{ github.event.workflow_run.pull_requests[0].number }}
GH_TOKEN: ${{ github.token }}
run: |
pr_author=$(gh api -H "Accept: application/vnd.github+json" \
/repos/${{ github.repository }}/pulls/${PR_NUMBER} \
--jq '.user.login')
pr_head_repo=$(gh api -H "Accept: application/vnd.github+json" \
/repos/${{ github.repository }}/pulls/${PR_NUMBER} \
--jq '.head.repo.full_name')
pr_head_branch=$(gh api -H "Accept: application/vnd.github+json" \
/repos/${{ github.repository }}/pulls/${PR_NUMBER} \
--jq '.head.ref')
{
echo "number=${PR_NUMBER}"
echo "author=${pr_author}"
echo "head_repo=${pr_head_repo}"
echo "head_branch=${pr_head_branch}"
} >> "$GITHUB_OUTPUT"
- name: Comment on PR with failure info
if: ${{ steps.pr_context.outputs.author == 'carlospolop' }}
uses: actions/github-script@v7
env:
PR_NUMBER: ${{ steps.pr_context.outputs.number }}
RUN_URL: ${{ github.event.workflow_run.html_url }}
WORKFLOW_NAME: ${{ github.event.workflow_run.name }}
with:
github-token: ${{ github.token }}
script: |
const prNumber = Number(process.env.PR_NUMBER);
const body = `PR #${prNumber} had a failing workflow "${process.env.WORKFLOW_NAME}".\n\nRun: ${process.env.RUN_URL}\n\nLaunching Codex to attempt a fix.`;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body,
});
- name: Checkout PR head
if: ${{ steps.pr_context.outputs.author == 'carlospolop' }}
uses: actions/checkout@v5
with:
repository: ${{ steps.pr_context.outputs.head_repo }}
ref: ${{ github.event.workflow_run.head_sha }}
fetch-depth: 0
persist-credentials: true
- name: Configure git author
if: ${{ steps.pr_context.outputs.author == 'carlospolop' }}
run: |
git config user.name "codex-action"
git config user.email "codex-action@users.noreply.github.com"
- name: Fetch failure summary
if: ${{ steps.pr_context.outputs.author == 'carlospolop' }}
env:
GH_TOKEN: ${{ github.token }}
RUN_ID: ${{ github.event.workflow_run.id }}
run: |
gh api -H "Accept: application/vnd.github+json" \
/repos/${{ github.repository }}/actions/runs/$RUN_ID/jobs \
--paginate > /tmp/jobs.json
python3 - <<'PY'
import json
data = json.load(open('/tmp/jobs.json'))
lines = []
for job in data.get('jobs', []):
if job.get('conclusion') == 'failure':
lines.append(f"Job: {job.get('name')} (id {job.get('id')})")
lines.append(f"URL: {job.get('html_url')}")
for step in job.get('steps', []):
if step.get('conclusion') == 'failure':
lines.append(f" Step: {step.get('name')}")
lines.append("")
summary = "\n".join(lines).strip() or "No failing job details found."
with open('codex_failure_summary.txt', 'w') as handle:
handle.write(summary)
PY
- name: Create Codex prompt
if: ${{ steps.pr_context.outputs.author == 'carlospolop' }}
env:
PR_NUMBER: ${{ steps.pr_context.outputs.number }}
RUN_URL: ${{ github.event.workflow_run.html_url }}
HEAD_BRANCH: ${{ steps.pr_context.outputs.head_branch }}
run: |
{
echo "You are fixing CI failures for PR #${PR_NUMBER} in ${{ github.repository }}."
echo "The failing workflow run is: ${RUN_URL}"
echo "The PR branch is: ${HEAD_BRANCH}"
echo ""
echo "Failure summary:"
cat codex_failure_summary.txt
echo ""
echo "Please identify the cause, apply a easy, simple and minimal fix, and update files accordingly."
echo "Run any fast checks you can locally (no network)."
echo "Leave the repo in a state ready to commit as when you finish, it'll be automatically committed and pushed."
} > codex_prompt.txt
- name: Run Codex
if: ${{ steps.pr_context.outputs.author == 'carlospolop' }}
id: run_codex
uses: openai/codex-action@v1
with:
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
prompt-file: codex_prompt.txt
sandbox: workspace-write
model: gpt-5.2-codex
- name: Commit and push if changed
if: ${{ steps.pr_context.outputs.author == 'carlospolop' }}
env:
TARGET_BRANCH: ${{ steps.pr_context.outputs.head_branch }}
PR_NUMBER: ${{ steps.pr_context.outputs.number }}
run: |
if git diff --quiet; then
echo "No changes to commit."
exit 0
fi
rm -f codex_failure_summary.txt codex_prompt.txt
git add -A
git reset -- codex_failure_summary.txt codex_prompt.txt
git commit -m "Fix CI failures for PR #${PR_NUMBER}"
git push origin HEAD:${TARGET_BRANCH}
- name: Comment with Codex result
if: ${{ steps.pr_context.outputs.author == 'carlospolop' && steps.run_codex.outputs.final-message != '' }}
uses: actions/github-script@v7
env:
PR_NUMBER: ${{ steps.pr_context.outputs.number }}
CODEX_MESSAGE: ${{ steps.run_codex.outputs.final-message }}
with:
github-token: ${{ github.token }}
script: |
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: Number(process.env.PR_NUMBER),
body: process.env.CODEX_MESSAGE,
});

0
LICENSE Executable file → Normal file
View File

3
README.md Executable file → Normal file
View File

@@ -28,7 +28,7 @@ Check the **[parsers](./parsers/)** directory to **transform PEASS outputs to JS
If you are a **PEASS & Hacktricks enthusiast**, you can get your hands now on **our [custom swag](https://peass.creator-spring.com/) and show how much you like our projects!**
You can also, join the 💬 [Discord group](https://discord.gg/hRep4RUj7f) or the [telegram group](https://t.me/peass) to learn about latest news in cybersecurity and meet other cybersecurity enthusiasts, or follow me on Twitter 🐦 [@hacktricks_live](https://twitter.com/hacktricks_live).
You can also, join the 💬 [Discord group](https://discord.gg/hRep4RUj7f) or the [telegram group](https://t.me/peass) to learn about the latest news in cybersecurity and meet other cybersecurity enthusiasts, or follow me on Twitter 🐦 [@hacktricks_live](https://twitter.com/hacktricks_live).
## Let's improve PEASS together
@@ -37,4 +37,3 @@ If you want to **add something** and have **any cool idea** related to this proj
## Advisory
All the scripts/binaries of the PEAS suite should be used for authorized penetration testing and/or educational purposes only. Any misuse of this software will not be the responsibility of the author or of any other collaborator. Use it at your own machines and/or with the owner's permission.

View File

@@ -1705,7 +1705,7 @@ search:
auto_check: True
exec:
- '( redis-server --version || echo_not_found "redis-server") 2>/dev/null'
- if [ "`redis-cli INFO 2>/dev/null`" ] && ! [ "`redis-cli INFO 2>/dev/null | grep -i NOAUTH`" ]; then echo "Redis isn't password protected" | sed -${E} "s,.*,${SED_RED},"; fi
- redis_info="$(if [ "$TIMEOUT" ]; then $TIMEOUT 2 redis-cli INFO 2>/dev/null; else redis-cli INFO 2>/dev/null; fi)"; if [ "$redis_info" ] && ! echo "$redis_info" | grep -i NOAUTH; then echo "Redis isn't password protected" | sed -${E} "s,.*,${SED_RED},"; fi
files:
- name: "redis.conf"
@@ -3352,7 +3352,7 @@ search:
- name: "credentials.xml"
value:
bad_regex: "secret.*|password.*"
bad_regex: "secret.*|password.*|token.*|SecretKey.*|credentialId.*"
remove_empty_lines: True
type: f
search_in:
@@ -3360,7 +3360,7 @@ search:
- name: "config.xml"
value:
bad_regex: "secret.*|password.*"
bad_regex: "secret.*|password.*|token.*|SecretKey.*|credentialId.*"
only_bad_lines: True
type: f
search_in:

View File

@@ -30,10 +30,9 @@
# Fat linpeas: 0
# Small linpeas: 0
if apt list --installed 2>/dev/null | grep -q 'polkit.*0\.105-26' || \
yum list installed 2>/dev/null | grep -q 'polkit.*\(0\.117-2\|0\.115-6\)' || \
rpm -qa 2>/dev/null | grep -q 'polkit.*\(0\.117-2\|0\.115-6\)'; then
if apt list --installed 2>/dev/null | grep -E 'polkit.*0\.105-26' | grep -qEv 'ubuntu1\.[1-9]' || \
yum list installed 2>/dev/null | grep -qE 'polkit.*\(0\.117-2\|0\.115-6\|0\.11[3-9]\)' || \
rpm -qa 2>/dev/null | grep -qE 'polkit.*\(0\.117-2\|0\.115-6\|0\.11[3-9]\)'; then
echo "Vulnerable to CVE-2021-3560" | sed -${E} "s,.*,${SED_RED_YELLOW},"
echo ""
fi

View File

@@ -30,7 +30,7 @@
# Functions Used: echo_not_found, print_2title, print_list, warn_exec
# Global Variables:
# Initial Functions:
# Generated Global Variables: $ASLR, $hypervisorflag, $detectedvirt, $unpriv_userns_clone, $perf_event_paranoid, $mmap_min_addr, $ptrace_scope, $dmesg_restrict, $kptr_restrict, $unpriv_bpf_disabled
# Generated Global Variables: $ASLR, $hypervisorflag, $detectedvirt, $unpriv_userns_clone, $perf_event_paranoid, $mmap_min_addr, $ptrace_scope, $dmesg_restrict, $kptr_restrict, $unpriv_bpf_disabled, $protected_symlinks, $protected_hardlinks
# Fat linpeas: 0
# Small linpeas: 0
@@ -127,6 +127,22 @@ else
if [ "$ptrace_scope" -eq 0 ]; then echo "0" | sed -${E} "s,0,${SED_RED},"; else echo "$ptrace_scope" | sed -${E} "s,.*,${SED_GREEN},g"; fi
fi
print_list "protected_symlinks? ............ "$NC
protected_symlinks=$(cat /proc/sys/fs/protected_symlinks 2>/dev/null)
if [ -z "$protected_symlinks" ]; then
echo_not_found "/proc/sys/fs/protected_symlinks"
else
if [ "$protected_symlinks" -eq 0 ]; then echo "0" | sed -${E} "s,0,${SED_RED},"; else echo "$protected_symlinks" | sed -${E} "s,.*,${SED_GREEN},g"; fi
fi
print_list "protected_hardlinks? ........... "$NC
protected_hardlinks=$(cat /proc/sys/fs/protected_hardlinks 2>/dev/null)
if [ -z "$protected_hardlinks" ]; then
echo_not_found "/proc/sys/fs/protected_hardlinks"
else
if [ "$protected_hardlinks" -eq 0 ]; then echo "0" | sed -${E} "s,0,${SED_RED},"; else echo "$protected_hardlinks" | sed -${E} "s,.*,${SED_GREEN},g"; fi
fi
print_list "perf_event_paranoid? ........... "$NC
perf_event_paranoid=$(cat /proc/sys/kernel/perf_event_paranoid 2>/dev/null)
if [ -z "$perf_event_paranoid" ]; then

View File

@@ -4,6 +4,7 @@
# Last Update: 07-03-2024
# Description: Check for additional disk information and system resources relevant to privilege escalation:
# - Disk utilization
# - Inode usage
# - System resources
# - Storage statistics
# - Common vulnerable scenarios:
@@ -44,4 +45,8 @@ if [ "$EXTRA_CHECKS" ] || [ "$DEBUG" ]; then
(df -h || lsblk) 2>/dev/null || echo_not_found "df and lsblk"
warn_exec free 2>/dev/null
echo ""
fi
print_2title "Inode usage"
warn_exec df -i 2>/dev/null
echo ""
fi

View File

@@ -1,14 +1,14 @@
# Title: Cloud - AWS ECS
# ID: CL_AWS_ECS
# Author: Carlos Polop
# Last Update: 22-08-2023
# Last Update: 17-01-2026
# Description: AWS ECS Enumeration
# License: GNU GPL
# Version: 1.0
# Functions Used: check_aws_ecs, exec_with_jq, print_2title, print_3title
# Global Variables: $aws_ecs_metadata_uri, $aws_ecs_service_account_uri, $is_aws_ecs
# Initial Functions: check_aws_ecs
# Generated Global Variables: $aws_ecs_req
# Generated Global Variables: $aws_ecs_req, $aws_exec_env, $ecs_task_metadata, $launch_type, $network_modes, $imds_tool, $imds_token, $imds_roles, $imds_http_code, $ecs_block_line, $ecs_host_line, $iptables_cmd, $docker_rules, $first_role
# Fat linpeas: 0
# Small linpeas: 1
@@ -44,5 +44,146 @@ if [ "$is_aws_ecs" = "Yes" ]; then
else
echo "I couldn't find AWS_CONTAINER_CREDENTIALS_RELATIVE_URI env var to get IAM role info (the task is running without a task role probably)"
fi
print_3title "ECS task metadata hints"
aws_exec_env=$(printenv AWS_EXECUTION_ENV 2>/dev/null)
if [ "$aws_exec_env" ]; then
printf "AWS_EXECUTION_ENV=%s\n" "$aws_exec_env"
fi
ecs_task_metadata=""
if [ "$aws_ecs_metadata_uri" ]; then
ecs_task_metadata=$(eval $aws_ecs_req "$aws_ecs_metadata_uri/task" 2>/dev/null)
fi
if [ "$ecs_task_metadata" ]; then
launch_type=$(printf "%s" "$ecs_task_metadata" | grep -oE '"LaunchType":"[^"]+"' | head -n 1 | cut -d '"' -f4)
if [ "$launch_type" ]; then
printf "ECS LaunchType reported: %s\n" "$launch_type"
fi
network_modes=$(printf "%s" "$ecs_task_metadata" | grep -oE '"NetworkMode":"[^"]+"' | cut -d '"' -f4 | sort -u | tr '\n' ' ')
if [ "$network_modes" ]; then
printf "Reported NetworkMode(s): %s\n" "$network_modes"
fi
else
echo "Unable to fetch task metadata (check ECS_CONTAINER_METADATA_URI)."
fi
echo ""
fi
print_3title "IMDS reachability from this task"
imds_token=""
imds_roles=""
imds_http_code=""
imds_tool=""
if command -v curl >/dev/null 2>&1; then
imds_tool="curl"
elif command -v wget >/dev/null 2>&1; then
imds_tool="wget"
fi
if [ "$imds_tool" = "curl" ]; then
imds_token=$(curl -s --connect-timeout 2 --max-time 2 -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600" 2>/dev/null)
if [ "$imds_token" ]; then
printf "[!] IMDSv2 token request succeeded (metadata reachable from this task).\n"
imds_roles=$(curl -s --connect-timeout 2 --max-time 2 -H "X-aws-ec2-metadata-token: $imds_token" "http://169.254.169.254/latest/meta-data/iam/security-credentials/" 2>/dev/null | tr '\n' ' ')
if [ "$imds_roles" ]; then
printf " Instance profile role(s) exposed via IMDS: %s\n" "$imds_roles"
first_role=$(printf "%s" "$imds_roles" | awk '{print $1}')
if [ "$first_role" ]; then
printf " Example: curl -H 'X-aws-ec2-metadata-token: <TOKEN>' http://169.254.169.254/latest/meta-data/iam/security-credentials/%s\n" "$first_role"
fi
else
printf " No IAM role names returned (instance profile might be missing).\n"
fi
else
imds_http_code=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 2 --max-time 2 "http://169.254.169.254/latest/meta-data/" 2>/dev/null)
case "$imds_http_code" in
000|"")
printf "[i] IMDS endpoint did not respond (likely blocked via hop-limit or host firewalling).\n"
;;
401)
printf "[i] IMDS requires v2 tokens but token requests are being blocked (bridge-mode tasks rely on this when hop limit = 1).\n"
;;
*)
printf "[i] IMDS GET returned HTTP %s (investigate host configuration).\n" "$imds_http_code"
;;
esac
fi
elif [ "$imds_tool" = "wget" ]; then
imds_token=$(wget -q -O - --timeout=2 --tries=1 --method=PUT --header="X-aws-ec2-metadata-token-ttl-seconds: 21600" "http://169.254.169.254/latest/api/token" 2>/dev/null)
if [ "$imds_token" ]; then
printf "[!] IMDSv2 token request succeeded (metadata reachable from this task).\n"
imds_roles=$(wget -q -O - --timeout=2 --tries=1 --header="X-aws-ec2-metadata-token: $imds_token" "http://169.254.169.254/latest/meta-data/iam/security-credentials/" 2>/dev/null | tr '\n' ' ')
if [ "$imds_roles" ]; then
printf " Instance profile role(s) exposed via IMDS: %s\n" "$imds_roles"
else
printf " No IAM role names returned (instance profile might be missing).\n"
fi
else
wget --server-response -O /dev/null --timeout=2 --tries=1 "http://169.254.169.254/latest/meta-data/" 2>&1 | awk 'BEGIN{code=""} /^ HTTP/{code=$2} END{ if(code!="") { printf("[i] IMDS GET returned HTTP %s (token could not be retrieved).\n", code); } else { print "[i] IMDS endpoint did not respond (likely blocked)."; } }'
fi
else
echo "Neither curl nor wget were found, I can't test IMDS reachability."
fi
echo ""
print_3title "ECS agent IMDS settings"
if [ -r "/etc/ecs/ecs.config" ]; then
ecs_block_line=$(grep -E "^ECS_AWSVPC_BLOCK_IMDS=" /etc/ecs/ecs.config 2>/dev/null | tail -n 1)
ecs_host_line=$(grep -E "^ECS_ENABLE_TASK_IAM_ROLE_NETWORK_HOST=" /etc/ecs/ecs.config 2>/dev/null | tail -n 1)
if [ "$ecs_block_line" ]; then
printf "%s\n" "$ecs_block_line"
if echo "$ecs_block_line" | grep -qi "=true"; then
echo " -> awsvpc-mode tasks should be blocked from IMDS by the ECS agent."
else
echo " -> awsvpc-mode tasks can still reach IMDS (set this to true to block)."
fi
else
echo "ECS_AWSVPC_BLOCK_IMDS not set (awsvpc tasks inherit host IMDS reachability)."
fi
if [ "$ecs_host_line" ]; then
printf "%s\n" "$ecs_host_line"
if echo "$ecs_host_line" | grep -qi "=false"; then
echo " -> Host-network tasks lose IAM task roles but IMDS is blocked."
else
echo " -> Host-network tasks keep IAM task roles and retain IMDS access."
fi
else
echo "ECS_ENABLE_TASK_IAM_ROLE_NETWORK_HOST not set (defaults keep IMDS reachable for host-mode tasks)."
fi
else
echo "Cannot read /etc/ecs/ecs.config (file missing or permissions denied)."
fi
echo ""
print_3title "DOCKER-USER IMDS filtering"
iptables_cmd=""
if command -v iptables >/dev/null 2>&1; then
iptables_cmd=$(command -v iptables)
elif command -v iptables-nft >/dev/null 2>&1; then
iptables_cmd=$(command -v iptables-nft)
fi
if [ "$iptables_cmd" ]; then
docker_rules=$($iptables_cmd -S DOCKER-USER 2>/dev/null)
if [ $? -eq 0 ]; then
if [ "$docker_rules" ]; then
echo "$docker_rules"
else
echo "(DOCKER-USER chain exists but no rules were found)"
fi
if echo "$docker_rules" | grep -q "169\\.254\\.169\\.254"; then
echo " -> IMDS traffic is explicitly filtered before Docker NAT."
else
echo " -> No DOCKER-USER rule drops 169.254.169.254 traffic (bridge tasks rely on hop limit or host firewalling)."
fi
else
echo "Unable to read DOCKER-USER chain (missing chain or insufficient permissions)."
fi
else
echo "iptables binary not found; cannot inspect DOCKER-USER chain."
fi
echo ""
fi

View File

@@ -6,7 +6,7 @@
# License: GNU GPL
# Version: 1.2
# Functions Used: echo_not_found, print_2title, print_info, print_3title
# Global Variables: $EXTRA_CHECKS, $SEARCH_IN_FOLDER, $IAMROOT, $WRITABLESYSTEMDPATH
# Global Variables: $EXTRA_CHECKS, $IAMROOT, $SEARCH_IN_FOLDER, $TIMEOUT, $WRITABLESYSTEMDPATH
# Initial Functions:
# Generated Global Variables: $service_unit, $service_path, $service_content, $finding, $findings, $service_file, $exec_path, $exec_paths, $service, $line, $target_file, $target_exec, $relpath1, $relpath2
# Fat linpeas: 0
@@ -178,7 +178,11 @@ if ! [ "$SEARCH_IN_FOLDER" ]; then
if [ "$EXTRA_CHECKS" ]; then
echo ""
print_3title "Service versions and status:"
(service --status-all || service -e || chkconfig --list || rc-status || launchctl list) 2>/dev/null || echo_not_found "service|chkconfig|rc-status|launchctl"
if [ "$TIMEOUT" ]; then
$TIMEOUT 30 sh -c "(service --status-all || service -e || chkconfig --list || rc-status || launchctl list) 2>/dev/null" || echo_not_found "service|chkconfig|rc-status|launchctl"
else
(service --status-all || service -e || chkconfig --list || rc-status || launchctl list) 2>/dev/null || echo_not_found "service|chkconfig|rc-status|launchctl"
fi
fi
# Check systemd path writability
@@ -190,4 +194,4 @@ if ! [ "$SEARCH_IN_FOLDER" ]; then
fi
echo ""
fi
fi

View File

@@ -0,0 +1,25 @@
# Title: Processes & Cron & Services & Timers - Deleted open files
# ID: PR_Deleted_open_files
# Author: Carlos Polop
# Last Update: 2025-01-07
# Description: Identify deleted files still held open by running processes
# License: GNU GPL
# Version: 1.0
# Functions Used: print_2title, print_info
# Global Variables: $DEBUG, $EXTRA_CHECKS, $E, $SED_RED
# Initial Functions:
# Generated Global Variables:
# Fat linpeas: 0
# Small linpeas: 1
if [ "$(command -v lsof 2>/dev/null || echo -n '')" ] || [ "$DEBUG" ]; then
print_2title "Deleted files still open"
print_info "Open deleted files can hide tools and still consume disk space"
lsof +L1 2>/dev/null | sed -${E} "s,\\(deleted\\),${SED_RED},g"
echo ""
elif [ "$EXTRA_CHECKS" ] || [ "$DEBUG" ]; then
print_2title "Deleted files still open"
print_info "lsof not found, scanning /proc for deleted file descriptors"
ls -l /proc/[0-9]*/fd 2>/dev/null | grep "(deleted)" | sed -${E} "s,\\(deleted\\),${SED_RED},g" | head -n 200
echo ""
fi

View File

@@ -23,6 +23,7 @@ if ! [ "$SEARCH_IN_FOLDER" ]; then
incrontab -l 2>/dev/null
ls -alR /etc/cron* /var/spool/cron/crontabs /var/spool/anacron 2>/dev/null | sed -${E} "s,$cronjobsG,${SED_GREEN},g" | sed "s,$cronjobsB,${SED_RED},g"
cat /etc/cron* /etc/at* /etc/anacrontab /var/spool/cron/crontabs/* /etc/incron.d/* /var/spool/incron/* 2>/dev/null | tr -d "\r" | grep -v "^#" | sed -${E} "s,$Wfolders,${SED_RED_YELLOW},g" | sed -${E} "s,$sh_usrs,${SED_LIGHT_CYAN}," | sed "s,$USER,${SED_LIGHT_MAGENTA}," | sed -${E} "s,$nosh_usrs,${SED_BLUE}," | sed "s,root,${SED_RED},"
grep -Hn '^PATH=' /etc/crontab /etc/cron.d/* 2>/dev/null | sed -${E} "s,$Wfolders,${SED_RED_YELLOW},g"
crontab -l -u "$USER" 2>/dev/null | tr -d "\r"
ls -lR /usr/lib/cron/tabs/ /private/var/at/jobs /var/at/tabs/ /etc/periodic/ 2>/dev/null | sed -${E} "s,$cronjobsG,${SED_GREEN},g" | sed "s,$cronjobsB,${SED_RED},g" #MacOS paths
atq 2>/dev/null
@@ -247,4 +248,4 @@ else
print_info "https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/index.html#scheduledcron-jobs"
find "$SEARCH_IN_FOLDER" '(' -type d -or -type f ')' '(' -name "cron*" -or -name "anacron" -or -name "anacrontab" -or -name "incron.d" -or -name "incron" -or -name "at" -or -name "periodic" ')' -exec echo {} \; -exec ls -lR {} \;
fi
echo ""
echo ""

View File

@@ -8,7 +8,7 @@
# Functions Used: print_2title, print_info
# Global Variables: $Groups, $groupsB, $groupsVB, $nosh_usrs, $sh_usrs, $USER
# Initial Functions:
# Generated Global Variables: $pkexec_bin, $policy_dir, $policy_file
# Generated Global Variables: $pkexec_bin, $pkexec_version, $policy_dir, $policy_file
# Fat linpeas: 0
# Small linpeas: 1
@@ -30,6 +30,10 @@ if [ -n "$pkexec_bin" ]; then
# Check polkit version for known vulnerabilities
if command -v pkexec >/dev/null 2>&1; then
pkexec --version 2>/dev/null
pkexec_version="$(pkexec --version 2>/dev/null | grep -oE '[0-9]+(\\.[0-9]+)+')"
if [ "$pkexec_version" ] && [ "$(printf '%s\n' "$pkexec_version" "0.120" | sort -V | head -n1)" = "$pkexec_version" ] && [ "$pkexec_version" != "0.120" ]; then
echo "Potentially vulnerable to CVE-2021-4034 (PwnKit) - check distro patches" | sed -${E} "s,.*,${SED_RED_YELLOW},"
fi
fi
fi

View File

@@ -6,7 +6,7 @@
# License: GNU GPL
# Version: 1.0
# Functions Used: print_2title
# Global Variables: $MACPEAS, $sh_usrs, $USER
# Global Variables: $MACPEAS, $sh_usrs, $TIMEOUT, $USER
# Initial Functions:
# Generated Global Variables: $ushell, $no_shells, $unexpected_shells
# Fat linpeas: 0
@@ -26,8 +26,16 @@ else
no_shells=$(grep -Ev "sh$" /etc/passwd 2>/dev/null | cut -d ':' -f 7 | sort | uniq)
unexpected_shells=""
printf "%s\n" "$no_shells" | while read f; do
if $f -c 'whoami' 2>/dev/null | grep -q "$USER"; then
unexpected_shells="$f\n$unexpected_shells"
if [ -x "$f" ]; then
if [ "$TIMEOUT" ]; then
if $TIMEOUT 1 "$f" -c 'whoami' 2>/dev/null | grep -q "$USER"; then
unexpected_shells="$f\n$unexpected_shells"
fi
else
if "$f" -c 'whoami' 2>/dev/null | grep -q "$USER"; then
unexpected_shells="$f\n$unexpected_shells"
fi
fi
fi
done
grep "sh$" /etc/passwd 2>/dev/null | sort | sed -${E} "s,$sh_usrs,${SED_LIGHT_CYAN}," | sed "s,$USER,${SED_LIGHT_MAGENTA}," | sed "s,root,${SED_RED},"
@@ -41,4 +49,4 @@ else
done
fi
fi
echo ""
echo ""

View File

@@ -8,7 +8,7 @@
# Functions Used: echo_not_found, print_2title, print_info
# Global Variables:$IAMROOT, $PASSWORD, $sudoB, $sudoG, $sudoVB1, $sudoVB2
# Initial Functions:
# Generated Global Variables:
# Generated Global Variables: $secure_path_line
# Fat linpeas: 0
# Small linpeas: 1
@@ -19,6 +19,16 @@ print_info "https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation
if [ "$PASSWORD" ]; then
(echo "$PASSWORD" | timeout 1 sudo -S -l | sed "s,_proxy,${SED_RED},g" | sed "s,$sudoG,${SED_GREEN},g" | sed -${E} "s,$sudoVB1,${SED_RED_YELLOW}," | sed -${E} "s,$sudoVB2,${SED_RED_YELLOW}," | sed -${E} "s,$sudoB,${SED_RED},g") 2>/dev/null || echo_not_found "sudo"
fi
(sudo -n -l 2>/dev/null | sed "s,_proxy,${SED_RED},g" | sed "s,$sudoG,${SED_GREEN},g" | sed -${E} "s,$sudoVB1,${SED_RED_YELLOW}," | sed -${E} "s,$sudoVB2,${SED_RED_YELLOW}," | sed -${E} "s,$sudoB,${SED_RED},g" | sed "s,\!root,${SED_RED},") 2>/dev/null || echo "No cached sudo token (sudo -n -l)"
secure_path_line=$(sudo -l 2>/dev/null | grep -o "secure_path=[^,]*" | head -n 1 | cut -d= -f2)
if [ "$secure_path_line" ]; then
for p in $(echo "$secure_path_line" | tr ':' ' '); do
if [ -w "$p" ]; then
echo "Writable secure_path entry: $p" | sed -${E} "s,.*,${SED_RED},g"
fi
done
fi
( grep -Iv "^$" cat /etc/sudoers | grep -v "#" | sed "s,_proxy,${SED_RED},g" | sed "s,$sudoG,${SED_GREEN},g" | sed -${E} "s,$sudoVB1,${SED_RED_YELLOW}," | sed -${E} "s,$sudoVB2,${SED_RED_YELLOW}," | sed -${E} "s,$sudoB,${SED_RED},g" | sed "s,pwfeedback,${SED_RED},g" ) 2>/dev/null || echo_not_found "/etc/sudoers"
if ! [ "$IAMROOT" ] && [ -w '/etc/sudoers.d/' ]; then
echo "You can create a file in /etc/sudoers.d/ and escalate privileges" | sed -${E} "s,.*,${SED_RED_YELLOW},"
@@ -29,4 +39,4 @@ for f in /etc/sudoers.d/*; do
grep -Iv "^$" "$f" | grep -v "#" | sed "s,_proxy,${SED_RED},g" | sed "s,$sudoG,${SED_GREEN},g" | sed -${E} "s,$sudoVB1,${SED_RED_YELLOW}," | sed -${E} "s,$sudoVB2,${SED_RED_YELLOW}," | sed -${E} "s,$sudoB,${SED_RED},g" | sed "s,pwfeedback,${SED_RED},g"
fi
done
echo ""
echo ""

View File

@@ -40,4 +40,18 @@ else
echo "ptrace protection is enabled ($ptrace_scope)" | sed "s,is enabled,${SED_GREEN},g";
fi
if [ -d "/var/run/sudo/ts" ]; then
echo "Sudo token directory perms:" | sed -${E} "s,.*,${SED_LIGHT_CYAN},g"
ls -ld /var/run/sudo/ts 2>/dev/null
if [ -w "/var/run/sudo/ts" ]; then
echo "/var/run/sudo/ts is writable" | sed -${E} "s,.*,${SED_RED},g"
fi
if [ -f "/var/run/sudo/ts/$USER" ]; then
ls -l "/var/run/sudo/ts/$USER" 2>/dev/null
if [ -w "/var/run/sudo/ts/$USER" ]; then
echo "User sudo token file is writable" | sed -${E} "s,.*,${SED_RED},g"
fi
fi
fi
echo ""

View File

@@ -0,0 +1,64 @@
# Title: Software Information - Browser Profiles
# ID: SW_Browser_profiles
# Author: Carlos Polop
# Last Update: 10-03-2025
# Description: List browser profiles that may store credentials/cookies
# License: GNU GPL
# Version: 1.0
# Functions Used: print_2title, print_3title, print_info
# Global Variables: $HOMESEARCH, $SED_RED
# Initial Functions:
# Generated Global Variables: $h, $firefox_ini, $chrome_base, $profiles
# Fat linpeas: 0
# Small linpeas: 1
print_2title "Browser Profiles"
print_info "https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/index.html#browser-data"
echo ""
for h in $HOMESEARCH; do
[ -d "$h" ] || continue
firefox_ini="$h/.mozilla/firefox/profiles.ini"
if [ -f "$firefox_ini" ]; then
print_3title "Firefox profiles ($h)"
awk -F= '
/^\[Profile/ { in_profile=1 }
/^Path=/ { path=$2 }
/^IsRelative=/ { isrel=$2 }
/^$/ {
if (path != "") {
if (isrel == "1") {
print base "/.mozilla/firefox/" path
} else {
print path
}
}
path=""; isrel=""
}
END {
if (path != "") {
if (isrel == "1") {
print base "/.mozilla/firefox/" path
} else {
print path
}
}
}
' base="$h" "$firefox_ini" 2>/dev/null | sed -${E} "s,.*,${SED_RED},"
echo ""
fi
for chrome_base in "$h/.config/google-chrome" "$h/.config/chromium" "$h/.config/BraveSoftware/Brave-Browser" "$h/.config/microsoft-edge" "$h/.config/microsoft-edge-beta" "$h/.config/microsoft-edge-dev"; do
if [ -d "$chrome_base" ]; then
profiles=$(find "$chrome_base" -maxdepth 1 -type d \( -name "Default" -o -name "Profile *" \) 2>/dev/null)
if [ "$profiles" ]; then
print_3title "Chromium profiles ($chrome_base)"
printf "%s\n" "$profiles" | sed -${E} "s,.*,${SED_RED},"
echo ""
fi
fi
done
done

View File

@@ -37,14 +37,14 @@ printf "%s\n" "$suids_files" | while read s; do
else
c="a"
for b in $sidB; do
if echo $s | grep -q $(echo $b | cut -d % -f 1); then
if echo "$sname" | grep -q $(echo $b | cut -d % -f 1); then
echo "$s" | sed -${E} "s,$(echo $b | cut -d % -f 1),${C}[1;31m& ---> $(echo $b | cut -d % -f 2)${C}[0m,"
c=""
break;
fi
done;
if [ "$c" ]; then
if echo "$s" | grep -qE "$sidG1" || echo "$s" | grep -qE "$sidG2" || echo "$s" | grep -qE "$sidG3" || echo "$s" | grep -qE "$sidG4" || echo "$s" | grep -qE "$sidVB" || echo "$s" | grep -qE "$sidVB2"; then
if echo "$sname" | grep -qE "$sidG1" || echo "$sname" | grep -qE "$sidG2" || echo "$sname" | grep -qE "$sidG3" || echo "$sname" | grep -qE "$sidG4" || echo "$sname" | grep -qE "$sidVB" || echo "$sname" | grep -qE "$sidVB2"; then
echo "$s" | sed -${E} "s,$sidG1,${SED_GREEN}," | sed -${E} "s,$sidG2,${SED_GREEN}," | sed -${E} "s,$sidG3,${SED_GREEN}," | sed -${E} "s,$sidG4,${SED_GREEN}," | sed -${E} "s,$sidVB,${SED_RED_YELLOW}," | sed -${E} "s,$sidVB2,${SED_RED_YELLOW},"
else
echo "$s (Unknown SUID binary!)" | sed -${E} "s,/.*,${SED_RED},"
@@ -96,4 +96,4 @@ printf "%s\n" "$suids_files" | while read s; do
fi
fi
done;
echo ""
echo ""

View File

@@ -17,10 +17,10 @@ check_external_hostname(){
INTERNET_SEARCH_TIMEOUT=15
# wget or curl?
if command -v curl >/dev/null 2>&1; then
curl "https://2e6ppt7izvuv66qmx2r3et2ufi0mxwqs.lambda-url.us-east-1.on.aws/" -H "User-Agent: linpeas" -d "{\"hostname\":\"$(hostname)\"}" -H "Content-Type: application/json" --max-time "$INTERNET_SEARCH_TIMEOUT"
curl "https://tools.hacktricks.wiki/api/host-checker" -H "User-Agent: linpeas" -d "{\"hostname\":\"$(hostname)\"}" -H "Content-Type: application/json" --max-time "$INTERNET_SEARCH_TIMEOUT"
elif command -v wget >/dev/null 2>&1; then
wget -q -O - "https://2e6ppt7izvuv66qmx2r3et2ufi0mxwqs.lambda-url.us-east-1.on.aws/" --header "User-Agent: linpeas" --post-data "{\"hostname\":\"$(hostname)\"}" -H "Content-Type: application/json" --timeout "$INTERNET_SEARCH_TIMEOUT"
wget -q -O - "https://tools.hacktricks.wiki/api/host-checker" --header "User-Agent: linpeas" --post-data "{\"hostname\":\"$(hostname)\"}" -H "Content-Type: application/json" --timeout "$INTERNET_SEARCH_TIMEOUT"
else
echo "wget or curl not found"
fi
}
}

View File

@@ -15,11 +15,12 @@
check_tcp_443_bin () {
local TIMEOUT_INTERNET_SECONDS_443_BIN=$1
local url_lambda="https://2e6ppt7izvuv66qmx2r3et2ufi0mxwqs.lambda-url.us-east-1.on.aws/"
local url_lambda="https://tools.hacktricks.wiki/api/host-checker"
if command -v curl >/dev/null 2>&1; then
if curl -s --connect-timeout $TIMEOUT_INTERNET_SECONDS_443_BIN "$url_lambda" \
-H "User-Agent: linpeas" -H "Content-Type: application/json" >/dev/null 2>&1
-H "User-Agent: linpeas" -H "Content-Type: application/json" \
-d "{\"hostname\":\"$(hostname)\"}" >/dev/null 2>&1
then
echo "Port 443 is accessible with curl"
return 0 # ✅ success
@@ -30,7 +31,8 @@ check_tcp_443_bin () {
elif command -v wget >/dev/null 2>&1; then
if wget -q --timeout=$TIMEOUT_INTERNET_SECONDS_443_BIN -O - "$url_lambda" \
--header "User-Agent: linpeas" -H "Content-Type: application/json" >/dev/null 2>&1
--header "User-Agent: linpeas" -H "Content-Type: application/json" \
--post-data "{\"hostname\":\"$(hostname)\"}" >/dev/null 2>&1
then
echo "Port 443 is accessible with wget"
return 0

View File

@@ -15,6 +15,5 @@
sidG1="/abuild-sudo$|/accton$|/allocate$|/ARDAgent$|/arping$|/atq$|/atrm$|/authpf$|/authpf-noip$|/authopen$|/batch$|/bbsuid$|/bsd-write$|/btsockstat$|/bwrap$|/cacaocsc$|/camel-lock-helper-1.2$|/ccreds_validate$|/cdrw$|/chage$|/check-foreground-console$|/chrome-sandbox$|/chsh$|/cons.saver$|/crontab$|/ct$|/cu$|/dbus-daemon-launch-helper$|/deallocate$|/desktop-create-kmenu$|/dma$|/dma-mbox-create$|/dmcrypt-get-device$|/doas$|/dotlockfile$|/dotlock.mailutils$|/dtaction$|/dtfile$|/eject$|/execabrt-action-install-debuginfo-to-abrt-cache$|/execdbus-daemon-launch-helper$|/execdma-mbox-create$|/execlockspool$|/execlogin_chpass$|/execlogin_lchpass$|/execlogin_passwd$|/execssh-keysign$|/execulog-helper$|/exim4|/expiry$|/fdformat$|/fstat$|/fusermount$|/fusermount3$"
sidG2="/gnome-pty-helper$|/glines$|/gnibbles$|/gnobots2$|/gnome-suspend$|/gnometris$|/gnomine$|/gnotski$|/gnotravex$|/gpasswd$|/gpg$|/gpio$|/gtali|/.hal-mtab-lock$|/helper$|/imapd$|/inndstart$|/kismet_cap_nrf_51822$|/kismet_cap_nxp_kw41z$|/kismet_cap_ti_cc_2531$|/kismet_cap_ti_cc_2540$|/kismet_cap_ubertooth_one$|/kismet_capture$|/kismet_cap_linux_bluetooth$|/kismet_cap_linux_wifi$|/kismet_cap_nrf_mousejack$|/ksu$|/list_devices$|/load_osxfuse$|/locate$|/lock$|/lockdev$|/lockfile$|/login_activ$|/login_crypto$|/login_radius$|/login_skey$|/login_snk$|/login_token$|/login_yubikey$|/lpc$|/lpd$|/lpd-port$|/lppasswd$|/lpq$|/lpr$|/lprm$|/lpset$|/lxc-user-nic$|/mahjongg$|/mail-lock$|/mailq$|/mail-touchlock$|/mail-unlock$|/mksnap_ffs$|/mlocate$|/mlock$|/mount$|/mount.cifs$|/mount.ecryptfs_private$|/mount.nfs$|/mount.nfs4$|/mount_osxfuse$|/mtr$|/mutt_dotlock$"
sidG3="/ncsa_auth$|/netpr$|/netkit-rcp$|/netkit-rlogin$|/netkit-rsh$|/netreport$|/netstat$|/newgidmap$|/newtask$|/newuidmap$|/nvmmctl$|/opieinfo$|/opiepasswd$|/pam_auth$|/pam_extrausers_chkpwd$|/pam_timestamp_check$|/pamverifier$|/pfexec$|/ping$|/ping6$|/pmconfig$|/pmap$|/polkit-agent-helper-1$|/polkit-explicit-grant-helper$|/polkit-grant-helper$|/polkit-grant-helper-pam$|/polkit-read-auth-helper$|/polkit-resolve-exe-helper$|/polkit-revoke-helper$|/polkit-set-default-helper$|/postdrop$|/postqueue$|/poweroff$|/ppp$|/procmail$|/pstat$|/pt_chmod$|/pwdb_chkpwd$|/quota$|/rcmd|/remote.unknown$|/rlogin$|/rmformat$|/rnews$|/run-mailcap$|/sacadm$|/same-gnome$|screen.real$|/security_authtrampoline$|/sendmail.sendmail$|/shutdown$|/skeyaudit$|/skeyinfo$|/skeyinit$|/sliplogin|/slocate$|/smbmnt$|/smbumount$|/smpatch$|/smtpctl$|/sperl5.8.8$|/ssh-agent$|/ssh-keysign$|/staprun$|/startinnfeed$|/stclient$|/su$|/suexec$|/sys-suspend$|/sysstat$|/systat$"
sidG3="/ncsa_auth$|/netpr$|/netkit-rcp$|/netkit-rlogin$|/netkit-rsh$|/netreport$|/netstat$|/newgidmap$|/newtask$|/newuidmap$|/nvmmctl$|/opieinfo$|/opiepasswd$|/pam_auth$|/pam_extrausers_chkpwd$|/pam_timestamp_check$|/pamverifier$|/pfexec$|/hping3$|/ping$|/ping6$|/pmconfig$|/pmap$|/polkit-agent-helper-1$|/polkit-explicit-grant-helper$|/polkit-grant-helper$|/polkit-grant-helper-pam$|/polkit-read-auth-helper$|/polkit-resolve-exe-helper$|/polkit-revoke-helper$|/polkit-set-default-helper$|/postdrop$|/postqueue$|/poweroff$|/ppp$|/procmail$|/pstat$|/pt_chmod$|/pwdb_chkpwd$|/quota$|/rcmd|/remote.unknown$|/rlogin$|/rmformat$|/rnews$|/run-mailcap$|/sacadm$|/same-gnome$|screen.real$|/security_authtrampoline$|/sendmail.sendmail$|/shutdown$|/skeyaudit$|/skeyinfo$|/skeyinit$|/sliplogin|/slocate$|/smbmnt$|/smbumount$|/smpatch$|/smtpctl$|/sperl5.8.8$|/ssh-agent$|/ssh-keysign$|/staprun$|/startinnfeed$|/stclient$|/su$|/suexec$|/sys-suspend$|/sysstat$|/systat$"
sidG4="/telnetlogin$|/timedc$|/tip$|/top$|/traceroute6$|/traceroute6.iputils$|/trpt$|/tsoldtlabel$|/tsoljdslabel$|/tsolxagent$|/ufsdump$|/ufsrestore$|/ulog-helper$|/umount.cifs$|/umount.nfs$|/umount.nfs4$|/unix_chkpwd$|/uptime$|/userhelper$|/userisdnctl$|/usernetctl$|/utempter$|/utmp_update$|/uucico$|/uuglist$|/uuidd$|/uuname$|/uusched$|/uustat$|/uux$|/uuxqt$|/VBoxHeadless$|/VBoxNetAdpCtl$|/VBoxNetDHCP$|/VBoxNetNAT$|/VBoxSDL$|/VBoxVolInfo$|/VirtualBoxVM$|/vmstat$|/vmware-authd$|/vmware-user-suid-wrapper$|/vmware-vmx$|/vmware-vmx-debug$|/vmware-vmx-stats$|/vncserver-x11$|/volrmmount$|/w$|/wall$|/whodo$|/write$|/X$|/Xorg.wrap$|/Xsun$|/Xvnc$|/yppasswd$"

View File

@@ -13,5 +13,5 @@
# Small linpeas: 1
sudoVB1=" \*|env_keep\W*\+=.*LD_PRELOAD|env_keep\W*\+=.*LD_LIBRARY_PATH|env_keep\W*\+=.*BASH_ENV|env_keep\W*\+=.* ENV|peass{SUDOVB1_HERE}"
sudoVB1=" \*|env_keep\W*\+=.*LD_PRELOAD|env_keep\W*\+=.*LD_LIBRARY_PATH|env_keep\W*\+=.*BASH_ENV|env_keep\W*\+=.* ENV|env_keep\W*\+=.*PATH|!env_reset|!requiretty|peass{SUDOVB1_HERE}"
sudoVB2="peass{SUDOVB2_HERE}"

View File

@@ -405,7 +405,7 @@ class LinpeasBuilder:
name = entry["name"]
caseinsensitive = entry.get("caseinsensitive", False)
regex = entry["regex"]
regex = regex.replace('"', '\\"').strip()
regex = regex.replace("\\", "\\\\").replace('"', '\\"').strip()
falsePositives = entry.get("falsePositives", False)
if falsePositives:

View File

@@ -127,7 +127,9 @@ def parse_line(line: str):
elif is_section(line, INFO_PATTERN):
title = parse_title(line)
C_SECTION["infos"].append(title)
if C_SECTION == {}:
return
C_SECTION.setdefault("infos", []).append(title)
#If here, then it's text
else:

View File

@@ -71,7 +71,7 @@ CALL :T_Progress 2
:ListHotFixes
where wmic >nul 2>&1
if %errorlevel% equ 0 (
wmic qfe get Caption,Description,HotFixID,InstalledOn | more
wmic qfe get Caption,Description,HotFixID,InstalledOn
) else (
powershell -command "Get-HotFix | Format-Table -AutoSize"
)
@@ -204,7 +204,7 @@ CALL :T_Progress 1
CALL :ColorLine " %E%33m[+]%E%97m Registered Anti-Virus(AV)"
where wmic >nul 2>&1
if %errorlevel% equ 0 (
WMIC /Node:localhost /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct Get displayName /Format:List | more
WMIC /Node:localhost /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct Get displayName /Format:List
) else (
powershell -command "Get-CimInstance -Namespace root/SecurityCenter2 -ClassName AntiVirusProduct | Select-Object -ExpandProperty displayName"
)
@@ -238,7 +238,7 @@ CALL :ColorLine " %E%33m[+]%E%97m MOUNTED DISKS"
ECHO. [i] Maybe you find something interesting
where wmic >nul 2>&1
if %errorlevel% equ 0 (
wmic logicaldisk get caption | more
wmic logicaldisk get caption
) else (
fsutil fsinfo drives
)
@@ -670,7 +670,7 @@ if "%long%" == "true" (
ECHO.
where wmic >nul 2>&1
if !errorlevel! equ 0 (
for /f %%x in ('wmic logicaldisk get name ^| more') do (
for /f %%x in ('wmic logicaldisk get name') do (
set tdrive=%%x
if "!tdrive:~1,2!" == ":" (
%%x

View File

@@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 3.16)
project(winPEAS_dotnet NONE)
set(PROJECT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/winPEAS.csproj")
find_program(DOTNET_EXECUTABLE dotnet)
find_program(MSBUILD_EXECUTABLE msbuild)
find_program(XBUILD_EXECUTABLE xbuild)
if(DOTNET_EXECUTABLE)
set(BUILD_TOOL "${DOTNET_EXECUTABLE}")
set(BUILD_ARGS build "${PROJECT_FILE}" -c Release)
elseif(MSBUILD_EXECUTABLE)
set(BUILD_TOOL "${MSBUILD_EXECUTABLE}")
set(BUILD_ARGS "${PROJECT_FILE}" /p:Configuration=Release)
elseif(XBUILD_EXECUTABLE)
set(BUILD_TOOL "${XBUILD_EXECUTABLE}")
set(BUILD_ARGS "${PROJECT_FILE}" /p:Configuration=Release)
else()
message(FATAL_ERROR "dotnet, msbuild, or xbuild is required to build winPEAS")
endif()
add_custom_target(winpeas ALL
COMMAND ${BUILD_TOOL} ${BUILD_ARGS}
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
)

View File

@@ -74,19 +74,15 @@ winpeas.exe -lolbas #Execute also additional LOLBAS search check
The goal of this project is to search for possible **Privilege Escalation Paths** in Windows environments.
New in this version:
- Detect potential GPO abuse by flagging writable SYSVOL paths for GPOs applied to the current host and by highlighting membership in the "Group Policy Creator Owners" group.
- Flag installed OEM utilities such as ASUS DriverHub, MSI Center, Acer Control Centre and Razer Synapse 4, highlighting writable updater folders and world-accessible pipes tied to recent CVEs.
It should take only a **few seconds** to execute almost all the checks and **some seconds/minutes during the lasts checks searching for known filenames** that could contain passwords (the time depened on the number of files in your home folder). By default only **some** filenames that could contain credentials are searched, you can use the **searchall** parameter to search all the list (this could will add some minutes).
The tool is based on **[SeatBelt](https://github.com/GhostPack/Seatbelt)**.
### New (AD-aware) checks
- Active Directory quick checks now include:
- gMSA readable managed passwords: enumerate msDS-GroupManagedServiceAccount objects and report those where the current user/group is allowed to retrieve the managed password (PrincipalsAllowedToRetrieveManagedPassword).
- AD object control surfaces: parse ACLs for high-value objects plus a sampled set of users/groups/computers and flag when the current security principal already has GenericAll/GenericWrite/WriteDacl/WriteOwner or attribute-specific rights (SPN, UAC, msDS-AllowedToActOnBehalfOfOtherIdentity, sidHistory, member, unicodePwd, replication) that can be abused for password resets, Kerberoasting, delegation/RBCD, DCSync, or stealth persistence.
- AD CS (ESC4) hygiene: enumerate published certificate templates and highlight templates where the current user/group has dangerous control rights (GenericAll/WriteDacl/WriteOwner/WriteProperty/ExtendedRight) that could allow template abuse (e.g., ESC4 -> ESC1).
These checks are lightweight, read-only, and only run when the host is domain-joined.
## Where are my COLORS?!?!?!
@@ -132,7 +128,7 @@ Once you have installed and activated it you need to:
- **System Information**
- [x] Basic System info information
- [x] Use Watson to search for vulnerabilities
- [x] Use WES-NG to search for vulnerabilities
- [x] Enumerate Microsoft updates
- [x] PS, Audit, WEF and LAPS Settings
- [x] LSA protection
@@ -266,7 +262,7 @@ Once you have installed and activated it you need to:
## TODO
- Add more checks
- Mantain updated Watson (last JAN 2021)
- Maintain updated WES-NG
If you want to help with any of this, you can do it using **[github issues](https://github.com/peass-ng/PEASS-ng/issues)** or you can submit a pull request.

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.DirectoryServices;
using System.Reflection;
using System.Linq;
using System.Security.AccessControl;
using System.Security.Principal;
@@ -20,6 +21,7 @@ namespace winPEAS.Checks
new List<Action>
{
PrintGmsaReadableByCurrentPrincipal,
PrintKerberoastableServiceAccounts,
PrintAdObjectControlPaths,
PrintAdcsMisconfigurations
}.ForEach(action => CheckRunner.Run(action, isDebug));
@@ -751,6 +753,37 @@ namespace winPEAS.Checks
}
}
private void PrintKerberoastableServiceAccounts()
{
try
{
Beaprint.MainPrint("Kerberoasting / service ticket risks");
Beaprint.LinkPrint("https://book.hacktricks.wiki/en/windows-hardening/active-directory-methodology/kerberoast.html",
"Enumerate weak SPN accounts and legacy Kerberos crypto");
if (!Checks.IsPartOfDomain)
{
Beaprint.GrayPrint(" [-] Host is not domain-joined. Skipping.");
return;
}
var defaultNC = GetRootDseProp("defaultNamingContext");
if (string.IsNullOrEmpty(defaultNC))
{
Beaprint.GrayPrint(" [-] Could not resolve defaultNamingContext.");
return;
}
PrintDomainKerberosDefaults(defaultNC);
EnumerateKerberoastCandidates(defaultNC);
}
catch (Exception ex)
{
Beaprint.GrayPrint(" [-] Kerberoasting check failed: " + ex.Message);
}
}
// Detect AD CS misconfigurations
private void PrintAdcsMisconfigurations()
{
@@ -939,5 +972,420 @@ namespace winPEAS.Checks
Beaprint.PrintException(ex.Message);
}
}
private void PrintDomainKerberosDefaults(string defaultNc)
{
try
{
using (var domainEntry = new DirectoryEntry("LDAP://" + defaultNc))
{
var encValue = GetDirectoryEntryInt(domainEntry, "msDS-DefaultSupportedEncryptionTypes");
if (encValue.HasValue)
{
var desc = DescribeEncTypes(encValue);
if (IsRc4Allowed(encValue))
Beaprint.BadPrint($" Domain default supported encryption types: {desc} — RC4/NT hash tickets allowed.");
else
Beaprint.GoodPrint($" Domain default supported encryption types: {desc} — RC4 disabled.");
}
else
{
Beaprint.GrayPrint(" [-] Domain default supported encryption types not set (legacy compatibility defaults to RC4).");
}
}
using (var baseDe = new DirectoryEntry("LDAP://" + defaultNc))
using (var ds = new DirectorySearcher(baseDe))
{
ds.Filter = "(&(objectClass=user)(sAMAccountName=krbtgt))";
ds.PropertiesToLoad.Add("msDS-SupportedEncryptionTypes");
var result = ds.FindOne();
if (result != null)
{
var encValue = GetIntProp(result, "msDS-SupportedEncryptionTypes");
if (encValue.HasValue)
{
var desc = DescribeEncTypes(encValue);
if (IsRc4Allowed(encValue))
Beaprint.BadPrint($" krbtgt supports: {desc} — RC4 TGTs can still be issued.");
else
Beaprint.GoodPrint($" krbtgt supports: {desc}.");
}
else
{
Beaprint.GrayPrint(" [-] krbtgt enc types inherit domain defaults (unspecified).");
}
}
}
}
catch (Exception ex)
{
Beaprint.GrayPrint(" [-] Unable to query Kerberos defaults: " + ex.Message);
}
}
private void EnumerateKerberoastCandidates(string defaultNc)
{
int checkedAccounts = 0;
int highTotal = 0;
int mediumTotal = 0;
var high = new List<KerberoastCandidate>();
var medium = new List<KerberoastCandidate>();
try
{
using (var baseDe = new DirectoryEntry("LDAP://" + defaultNc))
using (var ds = new DirectorySearcher(baseDe))
{
ds.PageSize = 500;
ds.Filter = "(servicePrincipalName=*)";
ds.PropertiesToLoad.Add("sAMAccountName");
ds.PropertiesToLoad.Add("displayName");
ds.PropertiesToLoad.Add("distinguishedName");
ds.PropertiesToLoad.Add("servicePrincipalName");
ds.PropertiesToLoad.Add("msDS-SupportedEncryptionTypes");
ds.PropertiesToLoad.Add("userAccountControl");
ds.PropertiesToLoad.Add("pwdLastSet");
ds.PropertiesToLoad.Add("memberOf");
ds.PropertiesToLoad.Add("objectClass");
foreach (SearchResult r in ds.FindAll())
{
checkedAccounts++;
var candidate = BuildKerberoastCandidate(r);
if (candidate == null)
{
continue;
}
if (candidate.IsHighRisk)
{
highTotal++;
if (high.Count < 15) high.Add(candidate);
}
else
{
mediumTotal++;
if (medium.Count < 12) medium.Add(candidate);
}
}
}
Beaprint.InfoPrint($"Checked {checkedAccounts} SPN-bearing accounts. High-risk RC4/privileged targets: {highTotal}, long-lived AES-only targets: {mediumTotal}.");
if (highTotal == 0 && mediumTotal == 0)
{
Beaprint.GoodPrint(" No obvious Kerberoastable service accounts detected with current visibility.");
return;
}
if (high.Count > 0)
{
Beaprint.BadPrint(" [!] RC4-enabled or privileged SPN accounts:");
foreach (var c in high)
{
Beaprint.ColorPrint($" - {c.Label} | SPNs: {c.SpnSummary} | Enc: {c.Encryption} | {c.Reason}", Beaprint.LRED);
}
if (highTotal > high.Count)
{
Beaprint.GrayPrint($" ... {highTotal - high.Count} additional high-risk accounts omitted.");
}
}
if (medium.Count > 0)
{
Beaprint.ColorPrint(" [~] Long-lived SPN accounts (still Kerberoastable via AES tickets):", Beaprint.YELLOW);
foreach (var c in medium)
{
Beaprint.ColorPrint($" - {c.Label} | SPNs: {c.SpnSummary} | Enc: {c.Encryption} | {c.Reason}", Beaprint.YELLOW);
}
if (mediumTotal > medium.Count)
{
Beaprint.GrayPrint($" ... {mediumTotal - medium.Count} additional medium-risk accounts omitted.");
}
}
}
catch (Exception ex)
{
Beaprint.GrayPrint(" [-] LDAP error while enumerating SPNs: " + ex.Message);
}
}
private KerberoastCandidate BuildKerberoastCandidate(SearchResult r)
{
var sam = GetProp(r, "sAMAccountName");
var displayName = GetProp(r, "displayName");
var dn = GetProp(r, "distinguishedName");
if (IsComputerObject(r) || IsManagedServiceAccount(r))
return null;
var uac = GetIntProp(r, "userAccountControl");
if (uac.HasValue && (uac.Value & 0x2) != 0)
return null;
var encValue = GetIntProp(r, "msDS-SupportedEncryptionTypes");
bool rc4Allowed = IsRc4Allowed(encValue);
bool aesPresent = HasAes(encValue);
bool passwordNeverExpires = uac.HasValue && (uac.Value & 0x10000) != 0;
DateTime? pwdLastSet = GetFileTimeProp(r, "pwdLastSet");
bool stalePassword = pwdLastSet.HasValue && pwdLastSet.Value < DateTime.UtcNow.AddDays(-365);
var privilegeHits = GetPrivilegedGroups(r);
var reasons = new List<string>();
if (rc4Allowed)
reasons.Add("RC4 allowed");
else if (!aesPresent)
reasons.Add("No AES flag");
if (passwordNeverExpires)
reasons.Add("PasswordNeverExpires");
if (stalePassword)
reasons.Add("PwdLastSet " + pwdLastSet.Value.ToString("yyyy-MM-dd"));
if (privilegeHits.Count > 0)
reasons.Add("Privileged: " + string.Join("/", privilegeHits));
if (reasons.Count == 0)
return null;
bool isHigh = rc4Allowed || privilegeHits.Count > 0;
if (!isHigh && !(passwordNeverExpires || stalePassword))
return null;
var label = !string.IsNullOrEmpty(sam) ? sam : dn;
if (!string.IsNullOrEmpty(displayName) && !string.Equals(displayName, sam, StringComparison.OrdinalIgnoreCase))
{
label = string.IsNullOrEmpty(sam) ? displayName : $"{sam} ({displayName})";
}
return new KerberoastCandidate
{
Label = label ?? "<unknown>",
SpnSummary = BuildSpnSummary(r),
Encryption = DescribeEncTypes(encValue),
Reason = string.Join("; ", reasons),
IsHighRisk = isHigh
};
}
private static string BuildSpnSummary(SearchResult r)
{
if (!r.Properties.Contains("servicePrincipalName") || r.Properties["servicePrincipalName"].Count == 0)
return "<none>";
var values = r.Properties["servicePrincipalName"];
var list = new List<string>();
int limit = values.Count < 3 ? values.Count : 3;
for (int i = 0; i < limit; i++)
{
var spn = values[i]?.ToString();
if (!string.IsNullOrEmpty(spn))
list.Add(spn);
}
string summary = list.Count > 0 ? string.Join(", ", list) : "<none>";
if (values.Count > limit)
summary += $" (+{values.Count - limit} more)";
return summary;
}
private static List<string> GetPrivilegedGroups(SearchResult r)
{
var hits = new List<string>();
if (!r.Properties.Contains("memberOf"))
return hits;
var memberships = r.Properties["memberOf"];
foreach (var membership in memberships)
{
var cn = ExtractCn(membership?.ToString());
if (string.IsNullOrEmpty(cn))
continue;
foreach (var keyword in PrivilegedGroupKeywords)
{
if (cn.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0)
{
if (!hits.Contains(cn))
hits.Add(cn);
break;
}
}
}
return hits;
}
private static string ExtractCn(string dn)
{
if (string.IsNullOrEmpty(dn))
return null;
var parts = dn.Split(',');
foreach (var part in parts)
{
var trimmed = part.Trim();
if (trimmed.StartsWith("CN=", StringComparison.OrdinalIgnoreCase))
return trimmed.Substring(3);
}
return dn;
}
private static bool IsComputerObject(SearchResult r)
{
return HasObjectClass(r, "computer");
}
private static bool IsManagedServiceAccount(SearchResult r)
{
return HasObjectClass(r, "msDS-ManagedServiceAccount") || HasObjectClass(r, "msDS-GroupManagedServiceAccount");
}
private static bool HasObjectClass(SearchResult r, string className)
{
if (!r.Properties.Contains("objectClass"))
return false;
foreach (var val in r.Properties["objectClass"])
{
if (string.Equals(val?.ToString(), className, StringComparison.OrdinalIgnoreCase))
return true;
}
return false;
}
private static DateTime? GetFileTimeProp(SearchResult r, string propName)
{
if (!r.Properties.Contains(propName) || r.Properties[propName].Count == 0)
return null;
return ConvertFileTime(r.Properties[propName][0]);
}
private static DateTime? ConvertFileTime(object value)
{
if (value == null)
return null;
try
{
if (value is long longVal)
{
if (longVal <= 0) return null;
return DateTime.FromFileTimeUtc(longVal);
}
if (value is IConvertible convertible)
{
long converted = convertible.ToInt64(null);
if (converted > 0)
return DateTime.FromFileTimeUtc(converted);
}
var type = value.GetType();
var highProp = type.GetProperty("HighPart", BindingFlags.Public | BindingFlags.Instance);
var lowProp = type.GetProperty("LowPart", BindingFlags.Public | BindingFlags.Instance);
if (highProp != null && lowProp != null)
{
int high = Convert.ToInt32(highProp.GetValue(value, null));
int low = Convert.ToInt32(lowProp.GetValue(value, null));
long fileTime = ((long)high << 32) | (uint)low;
if (fileTime > 0)
return DateTime.FromFileTimeUtc(fileTime);
}
}
catch
{
return null;
}
return null;
}
private static int? GetIntProp(SearchResult r, string name)
{
if (!r.Properties.Contains(name) || r.Properties[name].Count == 0)
return null;
return ConvertToNullableInt(r.Properties[name][0]);
}
private static int? GetDirectoryEntryInt(DirectoryEntry entry, string name)
{
try
{
return ConvertToNullableInt(entry.Properties[name]?.Value);
}
catch
{
return null;
}
}
private static int? ConvertToNullableInt(object value)
{
if (value == null)
return null;
if (value is int intValue)
return intValue;
if (value is long longValue)
return unchecked((int)longValue);
if (int.TryParse(value.ToString(), out var parsed))
return parsed;
return null;
}
private static bool IsRc4Allowed(int? encValue)
{
if (!encValue.HasValue || encValue.Value == 0)
return true;
return (encValue.Value & EncFlagRc4) != 0;
}
private static bool HasAes(int? encValue)
{
if (!encValue.HasValue)
return false;
return (encValue.Value & (EncFlagAes128 | EncFlagAes256)) != 0;
}
private static string DescribeEncTypes(int? encValue)
{
if (!encValue.HasValue || encValue.Value == 0)
return "Unspecified (inherits defaults / RC4 compatible)";
var parts = new List<string>();
if ((encValue.Value & EncFlagDesCrc) != 0) parts.Add("DES-CBC-CRC");
if ((encValue.Value & EncFlagDesMd5) != 0) parts.Add("DES-CBC-MD5");
if ((encValue.Value & EncFlagRc4) != 0) parts.Add("RC4-HMAC");
if ((encValue.Value & EncFlagAes128) != 0) parts.Add("AES128");
if ((encValue.Value & EncFlagAes256) != 0) parts.Add("AES256");
if ((encValue.Value & 0x20) != 0) parts.Add("FAST");
if (parts.Count == 0) parts.Add($"0x{encValue.Value:X}");
return string.Join(", ", parts);
}
private class KerberoastCandidate
{
public string Label { get; set; }
public string SpnSummary { get; set; }
public string Encryption { get; set; }
public string Reason { get; set; }
public bool IsHighRisk { get; set; }
}
private static readonly string[] PrivilegedGroupKeywords = new[]
{
"Domain Admin",
"Enterprise Admin",
"Administrators",
"Exchange",
"Schema Admin",
"Account Operator",
"Server Operator",
"Backup Operator",
"DnsAdmin"
};
private const int EncFlagDesCrc = 0x1;
private const int EncFlagDesMd5 = 0x2;
private const int EncFlagRc4 = 0x4;
private const int EncFlagAes128 = 0x8;
private const int EncFlagAes256 = 0x10;
}
}

View File

@@ -94,6 +94,7 @@ namespace winPEAS.Checks
new SystemCheck("activedirectoryinfo", new ActiveDirectoryInfo()),
new SystemCheck("cloudinfo", new CloudInfo()),
new SystemCheck("windowscreds", new WindowsCreds()),
new SystemCheck("registryinfo", new RegistryInfo()),
new SystemCheck("browserinfo", new BrowserInfo()),
new SystemCheck("filesinfo", new FilesInfo()),
new SystemCheck("fileanalysis", new FileAnalysis()),

View File

@@ -392,7 +392,7 @@ namespace winPEAS.Checks
foreach (string regHkcu in passRegHkcu)
{
Beaprint.DictPrint(RegistryHelper.GetRegValues("HKLM", regHkcu), false);
Beaprint.DictPrint(RegistryHelper.GetRegValues("HKCU", regHkcu), false);
}
foreach (string regHklm in passRegHklm)

View File

@@ -0,0 +1,141 @@
using System;
using System.Collections.Generic;
using System.Linq;
using winPEAS.Helpers;
using winPEAS.Helpers.Registry;
namespace winPEAS.Checks
{
internal class RegistryInfo : ISystemCheck
{
private const string TypingInsightsRelativePath = @"Software\Microsoft\Input\TypingInsights";
private static readonly string[] KnownWritableSystemKeyCandidates = new[]
{
@"SOFTWARE\Microsoft\CoreShell",
@"SOFTWARE\Microsoft\DRM",
@"SOFTWARE\Microsoft\Input\Locales",
@"SOFTWARE\Microsoft\Input\Settings",
@"SOFTWARE\Microsoft\Shell\Oobe",
@"SOFTWARE\Microsoft\Shell\Session",
@"SOFTWARE\Microsoft\Tracing",
@"SOFTWARE\Microsoft\Windows\UpdateApi",
@"SOFTWARE\Microsoft\WindowsUpdate\UX",
@"SOFTWARE\WOW6432Node\Microsoft\DRM",
@"SOFTWARE\WOW6432Node\Microsoft\Tracing",
@"SYSTEM\Software\Microsoft\TIP",
@"SYSTEM\ControlSet001\Control\Cryptography\WebSignIn\Navigation",
@"SYSTEM\ControlSet001\Control\MUI\StringCacheSettings",
@"SYSTEM\ControlSet001\Control\USB\AutomaticSurpriseRemoval",
@"SYSTEM\ControlSet001\Services\BTAGService\Parameters\Settings",
};
private static readonly string[] ScanBasePaths = new[]
{
@"SOFTWARE\Microsoft",
@"SOFTWARE\WOW6432Node\Microsoft",
@"SYSTEM\CurrentControlSet\Services",
@"SYSTEM\CurrentControlSet\Control",
@"SYSTEM\ControlSet001\Control",
};
public void PrintInfo(bool isDebug)
{
Beaprint.GreatPrint("Registry permissions for hive exploitation");
new List<Action>
{
PrintTypingInsightsPermissions,
PrintKnownSystemWritableKeys,
PrintHeuristicWritableKeys,
}.ForEach(action => CheckRunner.Run(action, isDebug));
}
private void PrintTypingInsightsPermissions()
{
Beaprint.MainPrint("Cross-user TypingInsights key (HKCU/HKU)");
var matches = new List<RegistryWritableKeyInfo>();
var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
if (RegistryAclScanner.TryGetWritableKey("HKCU", TypingInsightsRelativePath, out var currentUserKey))
{
if (seen.Add(currentUserKey.FullPath))
{
matches.Add(currentUserKey);
}
}
foreach (var sid in RegistryHelper.GetUserSIDs())
{
if (string.IsNullOrEmpty(sid) || sid.Equals(".DEFAULT", StringComparison.OrdinalIgnoreCase) || sid.EndsWith("_Classes", StringComparison.OrdinalIgnoreCase))
{
continue;
}
string relativePath = $"{sid}\\{TypingInsightsRelativePath}";
if (RegistryAclScanner.TryGetWritableKey("HKU", relativePath, out var info) && seen.Add(info.FullPath))
{
matches.Add(info);
}
}
if (matches.Count == 0)
{
Beaprint.GrayPrint(" [-] TypingInsights key does not grant write access to low-privileged groups.");
return;
}
PrintEntries(matches);
Beaprint.LinkPrint("https://projectzero.google/2025/05/the-windows-registry-adventure-8-exploitation.html", "Writable TypingInsights enables cross-user hive tampering and DoS.");
}
private void PrintKnownSystemWritableKeys()
{
Beaprint.MainPrint("Known HKLM descendants writable by standard users");
var matches = new List<RegistryWritableKeyInfo>();
foreach (var path in KnownWritableSystemKeyCandidates)
{
if (RegistryAclScanner.TryGetWritableKey("HKLM", path, out var info))
{
matches.Add(info);
}
}
if (matches.Count == 0)
{
Beaprint.GrayPrint(" [-] None of the tracked HKLM keys are writable by low-privileged groups.");
return;
}
PrintEntries(matches);
}
private void PrintHeuristicWritableKeys()
{
Beaprint.MainPrint("Sample of additional writable HKLM keys (depth-limited scan)");
var matches = RegistryAclScanner.ScanWritableKeys("HKLM", ScanBasePaths, maxDepth: 3, maxResults: 25);
if (matches.Count == 0)
{
Beaprint.GrayPrint(" [-] No additional writable HKLM keys were found within the sampled paths.");
return;
}
PrintEntries(matches);
Beaprint.GrayPrint(" [*] Showing up to 25 entries from the sampled paths to avoid noisy output.");
}
private static void PrintEntries(IEnumerable<RegistryWritableKeyInfo> entries)
{
foreach (var entry in entries)
{
var principals = string.Join(", ", entry.Principals);
var rights = entry.Rights.Count > 0 ? string.Join(", ", entry.Rights.Distinct(StringComparer.OrdinalIgnoreCase)) : "Write access";
var displayPath = string.IsNullOrEmpty(entry.FullPath) ? $"{entry.Hive}\\{entry.RelativePath}" : entry.FullPath;
Beaprint.BadPrint($" [!] {displayPath} -> {principals} ({rights})");
}
}
}
}

View File

@@ -36,6 +36,7 @@ namespace winPEAS.Checks
PrintModifiableServices,
PrintWritableRegServices,
PrintPathDllHijacking,
PrintOemPrivilegedUtilities,
PrintLegacySignedKernelDrivers,
PrintKernelQuickIndicators,
}.ForEach(action => CheckRunner.Run(action, isDebug));
@@ -210,6 +211,51 @@ namespace winPEAS.Checks
}
}
void PrintOemPrivilegedUtilities()
{
try
{
Beaprint.MainPrint("OEM privileged utilities & risky components");
var findings = OemSoftwareHelper.GetPotentiallyVulnerableComponents(Checks.CurrentUserSiDs);
if (findings.Count == 0)
{
Beaprint.GoodPrint(" None of the supported OEM utilities were detected.");
return;
}
foreach (var finding in findings)
{
bool hasCves = finding.Cves != null && finding.Cves.Length > 0;
string cveSuffix = hasCves ? $" ({string.Join(", ", finding.Cves)})" : string.Empty;
Beaprint.BadPrint($" {finding.Name}{cveSuffix}");
if (!string.IsNullOrWhiteSpace(finding.Description))
{
Beaprint.GrayPrint($" {finding.Description}");
}
foreach (var evidence in finding.Evidence)
{
string message = $" - {evidence.Message}";
if (evidence.Highlight)
{
Beaprint.BadPrint(message);
}
else
{
Beaprint.GrayPrint(message);
}
}
Beaprint.PrintLineSeparator();
}
}
catch (Exception ex)
{
Beaprint.PrintException(ex.Message);
}
}
void PrintLegacySignedKernelDrivers()
{
@@ -352,4 +398,3 @@ namespace winPEAS.Checks
}
}
}

View File

@@ -88,6 +88,7 @@ namespace winPEAS.Checks
PrintLocalGroupPolicy,
PrintPotentialGPOAbuse,
AppLockerHelper.PrintAppLockerPolicy,
PrintPrintNightmarePointAndPrint,
PrintPrintersWMIInfo,
PrintNamedPipes,
PrintNamedPipeAbuseCandidates,
@@ -836,6 +837,39 @@ namespace winPEAS.Checks
}
}
private static void PrintPrintNightmarePointAndPrint()
{
Beaprint.MainPrint("PrintNightmare PointAndPrint Policies");
Beaprint.LinkPrint("https://itm4n.github.io/printnightmare-exploitation/", "Check PointAndPrint policy hardening");
try
{
string key = @"Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint";
var restrict = RegistryHelper.GetDwordValue("HKLM", key, "RestrictDriverInstallationToAdministrators");
var noWarn = RegistryHelper.GetDwordValue("HKLM", key, "NoWarningNoElevationOnInstall");
var updatePrompt = RegistryHelper.GetDwordValue("HKLM", key, "UpdatePromptSettings");
if (restrict == null && noWarn == null && updatePrompt == null)
{
Beaprint.NotFoundPrint();
return;
}
Beaprint.NoColorPrint($" RestrictDriverInstallationToAdministrators: {restrict}\n" +
$" NoWarningNoElevationOnInstall: {noWarn}\n" +
$" UpdatePromptSettings: {updatePrompt}");
if (restrict == 0 && noWarn == 1 && updatePrompt == 2)
{
Beaprint.BadPrint(" [!] Potentially vulnerable to PrintNightmare misconfiguration");
}
}
catch (Exception ex)
{
Beaprint.PrintException(ex.Message);
}
}
private static void PrintPrintersWMIInfo()
{
Beaprint.MainPrint("Enumerating Printers (WMI)");

View File

@@ -132,6 +132,7 @@ namespace winPEAS.Helpers
Console.WriteLine(LCYAN + " activedirectoryinfo" + GRAY + " Quick AD checks (gMSA readable passwords, AD CS template rights)" + NOCOLOR);
Console.WriteLine(LCYAN + " cloudinfo" + GRAY + " Enumerate cloud information" + NOCOLOR);
Console.WriteLine(LCYAN + " windowscreds" + GRAY + " Search windows credentials" + NOCOLOR);
Console.WriteLine(LCYAN + " registryinfo" + GRAY + " Flag writable HKLM/HKU keys that enable hive tampering" + NOCOLOR);
Console.WriteLine(LCYAN + " browserinfo" + GRAY + " Search browser information" + NOCOLOR);
Console.WriteLine(LCYAN + " filesinfo" + GRAY + " Search generic files that can contains credentials" + NOCOLOR);
Console.WriteLine(LCYAN + " fileanalysis" + GRAY + " [NOT RUN BY DEFAULT] Search specific files that can contains credentials and for regexes inside files. Might take several minutes." + NOCOLOR);

View File

@@ -0,0 +1,221 @@
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.AccessControl;
using System.Security.Principal;
using winPEAS.Helpers;
namespace winPEAS.Helpers.Registry
{
internal class RegistryWritableKeyInfo
{
public string Hive { get; set; }
public string RelativePath { get; set; }
public string FullPath { get; set; }
public List<string> Principals { get; set; } = new List<string>();
public List<string> Rights { get; set; } = new List<string>();
}
internal static class RegistryAclScanner
{
private static readonly Dictionary<string, string> LowPrivSidMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{ new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null).Value, "BUILTIN\\Users" },
{ new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null).Value, "Authenticated Users" },
{ new SecurityIdentifier(WellKnownSidType.WorldSid, null).Value, "Everyone" },
{ new SecurityIdentifier(WellKnownSidType.InteractiveSid, null).Value, "Interactive" },
{ new SecurityIdentifier(WellKnownSidType.BuiltinGuestsSid, null).Value, "BUILTIN\\Guests" },
};
public static bool TryGetWritableKey(string hive, string relativePath, out RegistryWritableKeyInfo info)
{
info = null;
using (var key = OpenKey(hive, relativePath))
{
if (key == null)
{
return false;
}
return TryCollectWritableInfo(hive, relativePath, key, out info);
}
}
public static List<RegistryWritableKeyInfo> ScanWritableKeys(string hive, IEnumerable<string> basePaths, int maxDepth, int maxResults)
{
var results = new List<RegistryWritableKeyInfo>();
var seenPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var basePath in basePaths ?? Enumerable.Empty<string>())
{
if (results.Count >= maxResults)
{
break;
}
using (var key = OpenKey(hive, basePath))
{
if (key == null)
{
continue;
}
Traverse(hive, key, basePath, 0, maxDepth, maxResults, seenPaths, results);
}
}
return results;
}
private static void Traverse(string hive, RegistryKey currentKey, string currentPath, int depth, int maxDepth, int maxResults, HashSet<string> seenPaths, List<RegistryWritableKeyInfo> results)
{
if (currentKey == null || results.Count >= maxResults)
{
return;
}
if (TryCollectWritableInfo(hive, currentPath, currentKey, out var info))
{
if (seenPaths.Add(info.FullPath))
{
results.Add(info);
}
if (results.Count >= maxResults)
{
return;
}
}
if (depth >= maxDepth)
{
return;
}
string[] subKeys;
try
{
subKeys = currentKey.GetSubKeyNames();
}
catch
{
return;
}
foreach (var subKeyName in subKeys)
{
if (results.Count >= maxResults)
{
break;
}
try
{
using (var childKey = currentKey.OpenSubKey(subKeyName))
{
if (childKey == null)
{
continue;
}
string childPath = string.IsNullOrEmpty(currentPath) ? subKeyName : $"{currentPath}\\{subKeyName}";
Traverse(hive, childKey, childPath, depth + 1, maxDepth, maxResults, seenPaths, results);
}
}
catch
{
// Ignore keys we cannot open
}
}
}
private static bool TryCollectWritableInfo(string hive, string relativePath, RegistryKey key, out RegistryWritableKeyInfo info)
{
info = null;
try
{
var acl = key.GetAccessControl(AccessControlSections.Access);
var principals = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var rights = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (RegistryAccessRule rule in acl.GetAccessRules(true, true, typeof(SecurityIdentifier)))
{
if (rule.AccessControlType != AccessControlType.Allow)
{
continue;
}
var sid = rule.IdentityReference as SecurityIdentifier ?? rule.IdentityReference.Translate(typeof(SecurityIdentifier)) as SecurityIdentifier;
if (sid == null)
{
continue;
}
if (!LowPrivSidMap.TryGetValue(sid.Value, out var label))
{
continue;
}
string interestingRight = PermissionsHelper.PermInt2Str((int)rule.RegistryRights, PermissionType.WRITEABLE_OR_EQUIVALENT_REG);
if (string.IsNullOrEmpty(interestingRight))
{
continue;
}
principals.Add($"{label} ({sid.Value})");
rights.Add(interestingRight);
}
if (principals.Count == 0)
{
return false;
}
string normalizedRelativePath = relativePath ?? string.Empty;
string fullPath = string.IsNullOrEmpty(normalizedRelativePath) ? key.Name : $"{hive}\\{normalizedRelativePath}";
info = new RegistryWritableKeyInfo
{
Hive = hive,
RelativePath = normalizedRelativePath,
FullPath = fullPath,
Principals = principals.ToList(),
Rights = rights.ToList(),
};
return true;
}
catch
{
return false;
}
}
private static RegistryKey OpenKey(string hive, string path)
{
if (string.IsNullOrEmpty(path))
{
return null;
}
try
{
RegistryKey baseKey = hive switch
{
"HKLM" => Microsoft.Win32.Registry.LocalMachine,
"HKCU" => Microsoft.Win32.Registry.CurrentUser,
"HKU" => Microsoft.Win32.Registry.Users,
_ => null,
};
return baseKey?.OpenSubKey(path);
}
catch
{
return null;
}
}
}
}

View File

@@ -46,7 +46,7 @@ namespace winPEAS.Info.NetworkInfo
// 4. Call external checker
var resp = httpClient
.PostAsync("https://2e6ppt7izvuv66qmx2r3et2ufi0mxwqs.lambda-url.us-east-1.on.aws/", payload)
.PostAsync("https://tools.hacktricks.wiki/api/host-checker", payload)
.GetAwaiter().GetResult();
if (resp.IsSuccessStatusCode)

View File

@@ -4,6 +4,8 @@ using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Text.Json;
using System.Threading;
namespace winPEAS.Info.NetworkInfo
@@ -48,7 +50,7 @@ namespace winPEAS.Info.NetworkInfo
{ "1.1.1.1", "8.8.8.8" };
private const string LAMBDA_URL =
"https://2e6ppt7izvuv66qmx2r3et2ufi0mxwqs.lambda-url.us-east-1.on.aws/";
"https://tools.hacktricks.wiki/api/host-checker";
// Shared HttpClient (kept for HTTP & Lambda checks)
private static readonly HttpClient http = new HttpClient
@@ -118,7 +120,12 @@ namespace winPEAS.Info.NetworkInfo
using var cts =
new CancellationTokenSource(TimeSpan.FromMilliseconds(HTTP_TIMEOUT_MS));
var req = new HttpRequestMessage(HttpMethod.Get, LAMBDA_URL);
var payload = new StringContent(
JsonSerializer.Serialize(new { hostname = Environment.MachineName }),
Encoding.UTF8,
"application/json");
var req = new HttpRequestMessage(HttpMethod.Post, LAMBDA_URL);
req.Content = payload;
req.Headers.UserAgent.ParseAdd("winpeas");
req.Headers.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));

View File

@@ -0,0 +1,458 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Security.AccessControl;
using System.Security.Principal;
using System.ServiceProcess;
using winPEAS.Helpers;
namespace winPEAS.Info.ServicesInfo
{
internal static class OemSoftwareHelper
{
internal static List<OemSoftwareFinding> GetPotentiallyVulnerableComponents(Dictionary<string, string> currentUserSids)
{
var findings = new List<OemSoftwareFinding>();
var services = GetServiceSnapshot();
var processes = GetProcessSnapshot();
foreach (var definition in GetDefinitions())
{
var finding = new OemSoftwareFinding
{
Name = definition.Name,
Description = definition.Description,
Cves = definition.Cves,
};
AppendServiceEvidence(definition, services, finding);
AppendProcessEvidence(definition, processes, finding);
AppendPathEvidence(definition, currentUserSids, finding);
AppendPipeEvidence(definition, finding);
if (finding.Evidence.Count > 0)
{
findings.Add(finding);
}
}
return findings;
}
private static void AppendServiceEvidence(OemComponentDefinition definition, List<ServiceSnapshot> services, OemSoftwareFinding finding)
{
if (definition.ServiceHints == null || definition.ServiceHints.Length == 0)
{
return;
}
foreach (var serviceHint in definition.ServiceHints)
{
foreach (var service in services)
{
if (ContainsIgnoreCase(service.Name, serviceHint) ||
ContainsIgnoreCase(service.DisplayName, serviceHint))
{
finding.Evidence.Add(new OemEvidence
{
EvidenceType = "service",
Highlight = true,
Message = $"Service '{service.Name}' (Display: {service.DisplayName}) matches indicator '{serviceHint}'"
});
}
}
}
}
private static void AppendProcessEvidence(OemComponentDefinition definition, List<ProcessSnapshot> processes, OemSoftwareFinding finding)
{
if (definition.ProcessHints == null || definition.ProcessHints.Length == 0)
{
return;
}
foreach (var processHint in definition.ProcessHints)
{
foreach (var process in processes)
{
bool matchesName = ContainsIgnoreCase(process.Name, processHint);
bool matchesPath = ContainsIgnoreCase(process.FullPath, processHint);
if (matchesName || matchesPath)
{
var location = string.IsNullOrWhiteSpace(process.FullPath) ? "Unknown" : process.FullPath;
finding.Evidence.Add(new OemEvidence
{
EvidenceType = "process",
Highlight = true,
Message = $"Process '{process.Name}' (Path: {location}) matches indicator '{processHint}'"
});
}
}
}
}
private static void AppendPathEvidence(OemComponentDefinition definition, Dictionary<string, string> currentUserSids, OemSoftwareFinding finding)
{
if ((definition.DirectoryHints == null || definition.DirectoryHints.Length == 0) &&
(definition.FileHints == null || definition.FileHints.Length == 0))
{
return;
}
if (definition.DirectoryHints != null)
{
foreach (var dirHint in definition.DirectoryHints)
{
var expandedPath = ExpandPath(dirHint.Path);
if (!Directory.Exists(expandedPath))
{
continue;
}
var permissions = PermissionsHelper.GetPermissionsFolder(expandedPath, currentUserSids, PermissionType.WRITEABLE_OR_EQUIVALENT);
bool isWritable = permissions.Count > 0;
finding.Evidence.Add(new OemEvidence
{
EvidenceType = "path",
Highlight = isWritable,
Message = BuildPathMessage(expandedPath, dirHint.Description, isWritable, permissions)
});
}
}
if (definition.FileHints != null)
{
foreach (var fileHint in definition.FileHints)
{
var expandedPath = ExpandPath(fileHint);
if (!File.Exists(expandedPath))
{
continue;
}
var permissions = PermissionsHelper.GetPermissionsFile(expandedPath, currentUserSids, PermissionType.WRITEABLE_OR_EQUIVALENT);
bool isWritable = permissions.Count > 0;
finding.Evidence.Add(new OemEvidence
{
EvidenceType = "file",
Highlight = isWritable,
Message = BuildPathMessage(expandedPath, "file", isWritable, permissions)
});
}
}
}
private static void AppendPipeEvidence(OemComponentDefinition definition, OemSoftwareFinding finding)
{
if (definition.PipeHints == null)
{
return;
}
foreach (var pipeHint in definition.PipeHints)
{
try
{
var path = $"\\\\.\\pipe\\{pipeHint.Name}";
var security = File.GetAccessControl(path);
string sddl = security.GetSecurityDescriptorSddlForm(AccessControlSections.All);
string identity = string.Empty;
string rights = string.Empty;
bool worldWritable = false;
if (pipeHint.CheckWorldWritable)
{
worldWritable = HasWorldWritableAce(security, out identity, out rights);
}
string details = worldWritable
? $"Named pipe '{pipeHint.Name}' ({pipeHint.Description}) is writable by {identity} ({rights})."
: $"Named pipe '{pipeHint.Name}' ({pipeHint.Description}) present. SDDL: {sddl}";
finding.Evidence.Add(new OemEvidence
{
EvidenceType = "pipe",
Highlight = worldWritable,
Message = details
});
}
catch (FileNotFoundException)
{
// Pipe not present.
}
catch (DirectoryNotFoundException)
{
// Pipe namespace not accessible.
}
catch (Exception)
{
// Best effort: pipes might disappear during enumeration or deny access.
}
}
}
private static List<ServiceSnapshot> GetServiceSnapshot()
{
var services = new List<ServiceSnapshot>();
try
{
foreach (var service in ServiceController.GetServices())
{
services.Add(new ServiceSnapshot
{
Name = service.ServiceName ?? string.Empty,
DisplayName = service.DisplayName ?? string.Empty
});
}
}
catch (Exception)
{
// Ignore - this is best effort.
}
return services;
}
private static List<ProcessSnapshot> GetProcessSnapshot()
{
var processes = new List<ProcessSnapshot>();
try
{
foreach (var process in Process.GetProcesses())
{
string fullPath = string.Empty;
try
{
fullPath = process.MainModule?.FileName ?? string.Empty;
}
catch
{
// Access denied or 64-bit vs 32-bit mismatch.
}
processes.Add(new ProcessSnapshot
{
Name = process.ProcessName ?? string.Empty,
FullPath = fullPath ?? string.Empty
});
}
}
catch (Exception)
{
// Ignore - enumeration is best effort.
}
return processes;
}
private static string ExpandPath(string rawPath)
{
if (string.IsNullOrWhiteSpace(rawPath))
{
return string.Empty;
}
var expanded = Environment.ExpandEnvironmentVariables(rawPath);
return expanded.Trim().Trim('"');
}
private static string BuildPathMessage(string path, string description, bool isWritable, List<string> permissions)
{
string descriptor = string.IsNullOrWhiteSpace(description) ? "" : $" ({description})";
if (isWritable)
{
return $"Path '{path}'{descriptor} is writable by current user: {string.Join(", ", permissions)}";
}
return $"Path '{path}'{descriptor} detected.";
}
private static bool ContainsIgnoreCase(string value, string toFind)
{
if (string.IsNullOrWhiteSpace(value) || string.IsNullOrWhiteSpace(toFind))
{
return false;
}
return value.IndexOf(toFind, StringComparison.OrdinalIgnoreCase) >= 0;
}
private static bool HasWorldWritableAce(FileSecurity security, out string identity, out string rights)
{
identity = string.Empty;
rights = string.Empty;
try
{
var rules = security.GetAccessRules(true, true, typeof(SecurityIdentifier));
foreach (FileSystemAccessRule rule in rules)
{
if (rule.AccessControlType != AccessControlType.Allow)
{
continue;
}
if (rule.IdentityReference is SecurityIdentifier sid)
{
bool isWorld = sid.IsWellKnown(WellKnownSidType.WorldSid);
bool isAuthenticated = sid.IsWellKnown(WellKnownSidType.AuthenticatedUserSid);
if (!isWorld && !isAuthenticated)
{
continue;
}
const FileSystemRights interestingRights =
FileSystemRights.FullControl |
FileSystemRights.Modify |
FileSystemRights.Write |
FileSystemRights.WriteData |
FileSystemRights.CreateFiles |
FileSystemRights.ChangePermissions;
if ((rule.FileSystemRights & interestingRights) != 0)
{
identity = isWorld ? "Everyone" : "Authenticated Users";
rights = rule.FileSystemRights.ToString();
return true;
}
}
}
}
catch
{
// Ignore parsing issues.
}
return false;
}
private static IEnumerable<OemComponentDefinition> GetDefinitions()
{
return new List<OemComponentDefinition>
{
new OemComponentDefinition
{
Name = "ASUS DriverHub",
Description = "Local web API exposed by ADU.exe allowed bypassing origin/url validation and signature checks.",
Cves = new[] { "CVE-2025-3462", "CVE-2025-3463" },
ServiceHints = new[] { "asusdriverhub", "asus driverhub" },
ProcessHints = new[] { "adu", "asusdriverhub" },
DirectoryHints = new[]
{
new PathHint { Path = "%ProgramFiles%\\ASUS\\AsusDriverHub", Description = "Program Files" },
new PathHint { Path = "%ProgramFiles(x86)%\\ASUS\\AsusDriverHub", Description = "Program Files (x86)" },
new PathHint { Path = "%ProgramData%\\ASUS\\AsusDriverHub\\SupportTemp", Description = "SupportTemp updater staging" }
},
FileHints = new[]
{
"%ProgramData%\\ASUS\\AsusDriverHub\\SupportTemp\\Installer.json"
}
},
new OemComponentDefinition
{
Name = "MSI Center",
Description = "MSI.CentralServer.exe exposed TCP commands with TOCTOU and signature bypass issues.",
Cves = new[] { "CVE-2025-27812", "CVE-2025-27813" },
ServiceHints = new[] { "msi.center", "msi centralserver" },
ProcessHints = new[] { "msi.centralserver", "msi center" },
DirectoryHints = new[]
{
new PathHint { Path = "%ProgramFiles%\\MSI\\MSI Center", Description = "Main installation" },
new PathHint { Path = "%ProgramFiles(x86)%\\MSI\\MSI Center", Description = "Main installation (x86)" },
new PathHint { Path = "%ProgramData%\\MSI\\MSI Center", Description = "Shared data" },
new PathHint { Path = "%ProgramData%\\MSI Center SDK", Description = "SDK temp copy location" }
}
},
new OemComponentDefinition
{
Name = "Acer Control Centre",
Description = "ACCSvc.exe exposes treadstone_service_LightMode named pipe with weak impersonation controls.",
Cves = new[] { "CVE-2025-5491" },
ServiceHints = new[] { "accsvc", "acer control" },
ProcessHints = new[] { "accsvc", "accstd" },
DirectoryHints = new[]
{
new PathHint { Path = "%ProgramFiles%\\Acer\\Care Center", Description = "Install directory" },
new PathHint { Path = "%ProgramFiles(x86)%\\Acer\\Care Center", Description = "Install directory (x86)" }
},
PipeHints = new[]
{
new PipeHint { Name = "treadstone_service_LightMode", Description = "Command dispatcher", CheckWorldWritable = true }
}
},
new OemComponentDefinition
{
Name = "Razer Synapse 4 Elevation Service",
Description = "razer_elevation_service.exe exposes COM elevation helpers that allowed arbitrary process launch.",
Cves = new[] { "CVE-2025-27811" },
ServiceHints = new[] { "razer_elevation_service" },
ProcessHints = new[] { "razer_elevation_service" },
DirectoryHints = new[]
{
new PathHint { Path = "%ProgramFiles%\\Razer\\RazerAppEngine", Description = "Razer App Engine" },
new PathHint { Path = "%ProgramFiles(x86)%\\Razer\\RazerAppEngine", Description = "Razer App Engine (x86)" }
}
}
};
}
private class ServiceSnapshot
{
public string Name { get; set; }
public string DisplayName { get; set; }
}
private class ProcessSnapshot
{
public string Name { get; set; }
public string FullPath { get; set; }
}
private class OemComponentDefinition
{
public string Name { get; set; }
public string Description { get; set; }
public string[] Cves { get; set; } = Array.Empty<string>();
public string[] ServiceHints { get; set; } = Array.Empty<string>();
public string[] ProcessHints { get; set; } = Array.Empty<string>();
public PathHint[] DirectoryHints { get; set; } = Array.Empty<PathHint>();
public string[] FileHints { get; set; } = Array.Empty<string>();
public PipeHint[] PipeHints { get; set; } = Array.Empty<PipeHint>();
}
private class PathHint
{
public string Path { get; set; }
public string Description { get; set; }
}
private class PipeHint
{
public string Name { get; set; }
public string Description { get; set; }
public bool CheckWorldWritable { get; set; }
}
}
internal class OemSoftwareFinding
{
public string Name { get; set; }
public string Description { get; set; }
public string[] Cves { get; set; } = Array.Empty<string>();
public List<OemEvidence> Evidence { get; } = new List<OemEvidence>();
}
internal class OemEvidence
{
public string EvidenceType { get; set; }
public string Message { get; set; }
public bool Highlight { get; set; }
}
}

View File

@@ -16,6 +16,10 @@ namespace winPEAS.Info.UserInfo.SAM
{
get
{
if (_maxPasswordAge == long.MinValue)
{
return TimeSpan.MinValue;
}
return -new TimeSpan(_maxPasswordAge);
}
set
@@ -28,6 +32,10 @@ namespace winPEAS.Info.UserInfo.SAM
{
get
{
if (_minPasswordAge == long.MinValue)
{
return TimeSpan.MinValue;
}
return -new TimeSpan(_minPasswordAge);
}
set

View File

@@ -88,6 +88,10 @@ namespace winPEAS.KnownFileCreds
if (SID.StartsWith("S-1-5") && !SID.EndsWith("_Classes"))
{
string[] subKeys = RegistryHelper.GetRegSubkeys("HKU", string.Format("{0}\\Software\\SimonTatham\\PuTTY\\Sessions\\", SID));
if (subKeys.Length == 0)
{
subKeys = RegistryHelper.GetRegSubkeys("HKU", string.Format("{0}\\Software\\SimonTatham\\PuTTY\\Sessions", SID));
}
foreach (string sessionName in subKeys)
{
@@ -129,6 +133,10 @@ namespace winPEAS.KnownFileCreds
else
{
string[] subKeys = RegistryHelper.GetRegSubkeys("HKCU", "Software\\SimonTatham\\PuTTY\\Sessions\\");
if (subKeys.Length == 0)
{
subKeys = RegistryHelper.GetRegSubkeys("HKCU", "Software\\SimonTatham\\PuTTY\\Sessions");
}
RegistryKey selfKey = Registry.CurrentUser.OpenSubKey(@"Software\\SimonTatham\\PuTTY\\Sessions"); // extract own Sessions registry keys
if (selfKey != null)
@@ -198,6 +206,10 @@ namespace winPEAS.KnownFileCreds
if (SID.StartsWith("S-1-5") && !SID.EndsWith("_Classes"))
{
Dictionary<string, object> hostKeys = RegistryHelper.GetRegValues("HKU", string.Format("{0}\\Software\\SimonTatham\\PuTTY\\SshHostKeys\\", SID));
if ((hostKeys == null) || (hostKeys.Count == 0))
{
hostKeys = RegistryHelper.GetRegValues("HKU", string.Format("{0}\\Software\\SimonTatham\\PuTTY\\SshHostKeys", SID));
}
if ((hostKeys != null) && (hostKeys.Count != 0))
{
Dictionary<string, string> putty_ssh = new Dictionary<string, string>
@@ -216,6 +228,10 @@ namespace winPEAS.KnownFileCreds
else
{
Dictionary<string, object> hostKeys = RegistryHelper.GetRegValues("HKCU", "Software\\SimonTatham\\PuTTY\\SshHostKeys\\");
if ((hostKeys == null) || (hostKeys.Count == 0))
{
hostKeys = RegistryHelper.GetRegValues("HKCU", "Software\\SimonTatham\\PuTTY\\SshHostKeys");
}
if ((hostKeys != null) && (hostKeys.Count != 0))
{
Dictionary<string, string> putty_ssh = new Dictionary<string, string>();

View File

@@ -1201,6 +1201,7 @@
<Compile Include="Checks\SystemInfo.cs" />
<Compile Include="Checks\UserInfo.cs" />
<Compile Include="Checks\WindowsCreds.cs" />
<Compile Include="Checks\RegistryInfo.cs" />
<Compile Include="Helpers\AppLocker\AppLockerHelper.cs" />
<Compile Include="Helpers\AppLocker\AppLockerRules.cs" />
<Compile Include="Helpers\AppLocker\IAppIdPolicyHandler.cs" />
@@ -1462,12 +1463,14 @@
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Info\ServicesInfo\ServicesInfoHelper.cs" />
<Compile Include="Info\ServicesInfo\OemSoftwareHelper.cs" />
<Compile Include="Info\SystemInfo\SystemInfo.cs" />
<Compile Include="Info\UserInfo\UserInfoHelper.cs" />
<Compile Include="Helpers\DomainHelper.cs" />
<Compile Include="Helpers\CheckRunner.cs" />
<Compile Include="Helpers\ReflectionHelper.cs" />
<Compile Include="Helpers\Registry\RegistryHelper.cs" />
<Compile Include="Helpers\Registry\RegistryAclScanner.cs" />
<Compile Include="Helpers\Search\SearchHelper.cs" />
<Compile Include="Wifi\Wifi.cs" />
<Compile Include="Wifi\NativeWifiApi\Interop.cs" />

View File

@@ -815,12 +815,40 @@ systeminfo.exe
Write-Host ""
if ($TimeStamp) { TimeElapsed }
Write-Host -ForegroundColor Blue "=========|| WINDOWS HOTFIXES"
Write-Host "=| Check if windows is vulnerable with Watson https://github.com/rasta-mouse/Watson" -ForegroundColor Yellow
Write-Host "=| Check missing patches with WES-NG https://github.com/bitsadmin/wesng" -ForegroundColor Yellow
Write-Host "Possible exploits (https://github.com/codingo/OSCP-2/blob/master/Windows/WinPrivCheck.bat)" -ForegroundColor Yellow
$Hotfix = Get-HotFix | Sort-Object -Descending -Property InstalledOn -ErrorAction SilentlyContinue | Select-Object HotfixID, Description, InstalledBy, InstalledOn
$Hotfix | Format-Table -AutoSize
# PrintNightmare PointAndPrint policy checks
Write-Host ""
if ($TimeStamp) { TimeElapsed }
Write-Host -ForegroundColor Blue "=========|| PRINTNIGHTMARE POINTANDPRINT POLICY"
$pnKey = "HKLM:\Software\Policies\Microsoft\Windows NT\Printers\PointAndPrint"
if (Test-Path $pnKey) {
$pn = Get-ItemProperty -Path $pnKey -ErrorAction SilentlyContinue
$restrict = $pn.RestrictDriverInstallationToAdministrators
$noWarn = $pn.NoWarningNoElevationOnInstall
$updatePrompt = $pn.UpdatePromptSettings
Write-Host "RestrictDriverInstallationToAdministrators: $restrict"
Write-Host "NoWarningNoElevationOnInstall: $noWarn"
Write-Host "UpdatePromptSettings: $updatePrompt"
$hasAllValues = ($null -ne $restrict) -and ($null -ne $noWarn) -and ($null -ne $updatePrompt)
if (-not $hasAllValues) {
Write-Host "PointAndPrint policy values are missing or not configured" -ForegroundColor Gray
} elseif (($restrict -eq 0) -and ($noWarn -eq 1) -and ($updatePrompt -eq 2)) {
Write-Host "Potentially vulnerable to PrintNightmare misconfiguration" -ForegroundColor Red
} else {
Write-Host "PointAndPrint policy is not in the known risky configuration" -ForegroundColor Green
}
} else {
Write-Host "PointAndPrint policy key not found" -ForegroundColor Gray
}
#Show all unique updates installed
Write-Host ""
if ($TimeStamp) { TimeElapsed }