Skip to content

Commit f730545

Browse files
committed
feat(cli): Added --download-auto and renamed --download to --download-link
1 parent 4325bc7 commit f730545

7 files changed

Lines changed: 36 additions & 24 deletions

File tree

‎README.md‎

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ It then puts the resulting string in a template file, that contains the code to
99

1010
Currently, there are multiple actions implemented, that can be executed, after the payload is decoded:
1111

12-
- `--download`: Download the payload as a file when clicking a button (example use case: bypass antivirus / filters)
13-
- @TODO: `--download-auto`: Automatically download the payload as a file when the page is opened (example use case: bypass antivirus / filters)
12+
- `--download-link`: Download the payload as a file when clicking a button (example use case: bypass antivirus / filters)
13+
- `--download-auto`: Automatically download the payload as a file when the page is opened (example use case: bypass antivirus / filters)
1414
- `--driveby-redirect <REDIRECT_URL>`: Perform a drive-by download and redirect to the `REDIRECT_URL` (example use case: phishing)
1515
- `--eval`: Execute payload as JavaScript code (example use case: obfuscate malicious JS code)
1616
- `--replace`: Show payload as HTML page (example use case: compress a big web page)
@@ -30,7 +30,7 @@ Ascii85 encoding | yes | yes
3030
GZIP compression | yes, always | yes, can be disabled
3131
AES-GCM encryption | no | yes
3232
Automatic detection of most efficient algorithms | no | yes
33-
Payload actions | download, eval, replace | all
33+
Payload actions | download-link, eval, replace | all
3434

3535
## Python version
3636

@@ -56,14 +56,14 @@ python3 -m pip install .
5656

5757
Example usage of the pip package:
5858
```bash
59-
self-unzip-html html --download -o psexec.html -i PsExec.exe
59+
self-unzip-html html --download-auto -o psexec.html -i PsExec.exe
6060
```
6161

6262
#### Docker
6363

6464
You can use the image pushed to ghcr.io:
6565
```bash
66-
docker run --rm -v "$PWD:/share" ghcr.io/six-two/self-unzip-html html --download -o psexec.html -i ./PsExec.exe
66+
docker run --rm -v "$PWD:/share" ghcr.io/six-two/self-unzip-html html --download-auto -o psexec.html -i ./PsExec.exe
6767
```
6868

6969
To use the bleeding edge version (`main` branch), you can build the `Dockerfile`:
@@ -73,24 +73,24 @@ docker build -t self-unzip-html .
7373

7474
Usage of docker image:
7575
```bash
76-
docker run --rm -v "$PWD:/share" self-unzip-html html --download -o psexec.html -i ./PsExec.exe
76+
docker run --rm -v "$PWD:/share" self-unzip-html html --download-auto -o psexec.html -i ./PsExec.exe
7777
```
7878

7979
### Usage
8080

8181
Do a basic HTML smuggling, that will show download link for an executable file:
8282
```bash
83-
self-unzip-html html --download -o psexec.html -i PsExec.exe
83+
self-unzip-html html --download-auto -o psexec.html -i PsExec.exe
8484
```
8585

8686
Or if you wanted to password-protect the output:
8787
```bash
88-
self-unzip-html encrypted-html --download -o psexec_encrypted.html -p YourPasswordHere -i PsExec.exe
88+
self-unzip-html encrypted-html --download-auto -o psexec_encrypted.html -p YourPasswordHere -i PsExec.exe
8989
```
9090

9191
Instead of HTML pages, you can also embed an HTML smuggling payload in an SVG file:
9292
```bash
93-
self-unzip-html svg -i PsExec.exe --download -o psexec.svg
93+
self-unzip-html svg -i PsExec.exe --download-auto -o psexec.svg
9494
```
9595

9696
Please note that not all payload actions are available for SVGs due to technical limitations.
@@ -105,7 +105,7 @@ If you want to use it for phishing (sending a download link to a malicious file)
105105

106106
![Driveby Redirect Screenshot](./driveby-redirect-screenshot.png)
107107

108-
Just search for a "thank you for downloading" page that does not start a download. There are many of them for software like Skype, AnyDesk, etc. Then rename your payload file to something an visitor of the download page would expect and create the HTML smuggling page:
108+
Just search for a "thank you for downloading" page that does not start a download. There are many of them for software like Skype, AnyDesk, etc. Then rename your payload file to something a visitor of the download page would expect and create the HTML smuggling page:
109109
```bash
110110
self-unzip-html html -i AnyDesk.exe -o anydesk-download.html --driveby-redirect https://anydesk.com/en/downloads/guide/thank-you --obscure-action
111111
```
@@ -193,6 +193,7 @@ The rest of the project is under the MIT license, so you can do whatever as long
193193

194194
### Head
195195

196+
- Renamed `--download` to `--download-link` and added `--download-auto` which automatically starts the download
196197
- Added `--copy-text` and `--copy-base64` options for smuggling files when downloads are not possible (for example in remote browsing setups)
197198
- Added `serve` subcommand that serves the current directory and allows downloading files directly or via HTML smuggling. Each file in a directory listing has an entry like `.DS_Store (HTML, SVG)`.
198199
- Refactoring of the python code to make it usable as a library
@@ -203,7 +204,7 @@ The rest of the project is under the MIT license, so you can do whatever as long
203204
- Added `--cache-password` option
204205
- Added option to supply decryption password via `localStorage`: `localStorage.setItem("self_unzip_pw", "YOUR_PASSWORD_HERE")`
205206
- Added `--encoding hex` option
206-
- Added `--svg` option and ported the `--download` option to work in SVGs
207+
- Added support for SVG output files
207208

208209
### Version 0.2.1
209210

‎python/self_unzip_html/cli/__init__.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,10 @@ def main_wrapped() -> None:
4444

4545
args = ap.parse_args()
4646
# Set default values to prevent AttributeErrors later on
47+
# Needs to be done for all flags (mainly actions) that are not always available
4748
add_if_does_not_exist(args, "password", None)
4849
add_if_does_not_exist(args, "replace", False)
50+
add_if_does_not_exist(args, "download_link", None)
4951
add_if_does_not_exist(args, "copy_base64", False)
5052
add_if_does_not_exist(args, "copy_text", False)
5153

‎python/self_unzip_html/cli/action.py‎

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,19 @@
22
from typing import Any
33
# local
44
from . import Subcommand
5-
from ..static_js import JS_DOWNLOAD, JS_DOWNLOAD_SVG, JS_DRIVEBY_REDIRECT, JS_DRIVEBY_REDIRECT_SVG, JS_EVAL, JS_REPLACE, JS_SHOW_TEXT, JS_SHOW_TEXT_SVG, JS_COPY_TEXT
5+
from ..static_js import JS_DOWNLOAD_LINK, JS_DOWNLOAD_AUTO, JS_DOWNLOAD_SVG, JS_DRIVEBY_REDIRECT, JS_DRIVEBY_REDIRECT_SVG, JS_EVAL, JS_REPLACE, JS_SHOW_TEXT, JS_SHOW_TEXT_SVG, JS_COPY_TEXT
66
from ..minified_js import COPY_BASE64
7-
from ..util import OperationNotImplemented
7+
8+
NO_ARG="NO_ARGUMENT_SUPPLIED"
89

910
def register_action_argument_parser(ap: ArgumentParser, subcommand: Subcommand):
1011
if subcommand != Subcommand.SERVE: #@TODO: figure out how to do it later
1112
payload_option_visual_group = ap.add_argument_group("Payload Action")
1213
payload_option_mutex = payload_option_visual_group.add_mutually_exclusive_group(required=True)
13-
payload_option_mutex.add_argument("--download", nargs="?", metavar="FILE_NAME", const="", help="show a download link to download the payload as a file. If you specify an argument that is used as the name of the file to download")
14+
if subcommand not in [Subcommand.SVG, Subcommand.SVG_ENCRYPTED]:
15+
payload_option_mutex.add_argument("--download-link", nargs="?", metavar="FILE_NAME", const=NO_ARG, help="show a download link to download the payload as a file. If you specify an argument that is used as the name of the file to download")
16+
17+
payload_option_mutex.add_argument("--download-auto", nargs="?", metavar="FILE_NAME", const=NO_ARG, help="show a download link to download the payload as a file. If you specify an argument that is used as the name of the file to download")
1418
payload_option_mutex.add_argument("--eval", action="store_true", help="pass the payload to eval() to run it as JavaScript code")
1519
if subcommand not in [Subcommand.SVG, Subcommand.SVG_ENCRYPTED]:
1620
# Setting the innerHTML of a svg.text always resulted in errors. So we do not show this option with SVGs
@@ -27,10 +31,15 @@ def register_action_argument_parser(ap: ArgumentParser, subcommand: Subcommand):
2731

2832

2933
def get_javascript(args: Any, file_name: str, is_svg: bool) -> str:
30-
if args.download != None:
31-
# If args.downlaod is empty, use the name of the input file, otherwise use the user provided value
32-
base_code = JS_DOWNLOAD_SVG if is_svg else JS_DOWNLOAD
33-
return base_code.replace("{{NAME}}", args.download or file_name)
34+
if args.download_link != None:
35+
# If argument is empty, use the name of the input file, otherwise use the user provided value
36+
download_file_name = file_name if args.download_link == NO_ARG else args.download_link
37+
return JS_DOWNLOAD_LINK.replace("{{NAME}}", download_file_name)
38+
elif args.download_auto != None:
39+
# If argument is empty, use the name of the input file, otherwise use the user provided value
40+
base_code = JS_DOWNLOAD_SVG if is_svg else JS_DOWNLOAD_AUTO
41+
download_file_name = file_name if args.download_auto == NO_ARG else args.download_auto
42+
return base_code.replace("{{NAME}}", download_file_name)
3443
elif args.eval:
3544
return JS_EVAL
3645
elif args.replace:

‎python/self_unzip_html/cli/server.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from .output import get_compression_list, get_encoding_list
1414
from ..template import get_html_template, get_svg_template, DEFAULT_HTML_TEMPLATE_PATH
1515
from ..page_builder import PageBuilder
16-
from ..static_js import JS_DOWNLOAD, JS_DOWNLOAD_SVG
16+
from ..static_js import JS_DOWNLOAD_LINK, JS_DOWNLOAD_SVG
1717

1818

1919
def register_server_argument_parser(ap: ArgumentParser, subcommand: Subcommand):
@@ -94,7 +94,7 @@ def serve_html(self, path):
9494
with open(path, "rb") as f:
9595
file_contents = f.read()
9696

97-
file_contents = self.build_page(file_name, file_contents, self.server.html_template, JS_DOWNLOAD, False)
97+
file_contents = self.build_page(file_name, file_contents, self.server.html_template, JS_DOWNLOAD_LINK, False)
9898

9999
self.send_response(200)
100100
self.send_header("Content-type", "text/html; charset=utf-8")

‎python/self_unzip_html/crypto_aes.py‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ def deriveKey(password: bytes, iv: bytes, pbkdf_iteration_count: int):
1919
salt_pre_hash_bytes = password + application_name + iv
2020
salt = sha256(salt_pre_hash_bytes).digest()
2121
iteration_count = pbkdf_iteration_count + len(password) + iv[0]
22-
# print("[Debug] Salt:", salt.hex())
23-
# print(iteration_count)
2422

2523
return pbkdf2_hmac("sha256", password, salt, iteration_count, BITS_256)
2624

‎python/self_unzip_html/static_js.py‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626

2727
JS_COPY_TEXT = """setTimeout(()=>{let d=document.open(); let t=new TextDecoder().decode(og_data); d.write('<h2>Copy file as text</h2><button>copy</button>'); let b=d.querySelector("button"); b.onclick=()=>{navigator.clipboard.writeText(t).then(()=>{b.textContent="copied";setTimeout(()=>b.textContent="copy",2000);}).catch(e=>{console.error(e);b.textContent="copy failed";setTimeout(()=>b.textContent="copy",2000);})}}, 50);"""
2828

29-
JS_DOWNLOAD = 'let b=new Blob([og_data],{type:"application/octet-stream"});let u=URL.createObjectURL(b);document.body.innerHTML=`<h1>Unpacked {{NAME}}</h1><a href="${u}" download="{{NAME}}">Click here to download</a>`'
29+
JS_DOWNLOAD_LINK = 'let b=new Blob([og_data],{type:"application/octet-stream"});let u=URL.createObjectURL(b);document.body.innerHTML=`<h1>Unpacked {{NAME}}</h1><a href="${u}" download="{{NAME}}">Click here to download</a>`'
30+
31+
JS_DOWNLOAD_AUTO = 'let b=new Blob([og_data],{type:"application/octet-stream"});let u=URL.createObjectURL(b);document.body.innerHTML=`<a href="${u}" download="{{NAME}}" id="auto-click">Click here if the download does not start automatically</a>`;setTimeout(()=>document.getElementById("auto-click").click(),50)'
3032

3133
JS_DOWNLOAD_SVG = 'let b=new Blob([og_data],{type:"application/octet-stream"});let u=URL.createObjectURL(b);let a=document.createElementNS("http://www.w3.org/1999/xhtml","a");document.querySelector("svg").appendChild(a);a.href=u;a.download="{{NAME}}";a.click()'
3234

‎test.sh‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ self-unzip-html encrypted-html -i README.md -o test_b64_encrypt.html -e base64 -
3535
self-unzip-html encrypted-html -i README.md -o test_b85_gzip_encrypt.html -e ascii85 -c gzip -p test --replace
3636
# other modes
3737
echo 'alert("Looks like it still works :)")' | self-unzip-html html -i - -o test_eval_b64_gzip.html -e base64 -c gzip --eval
38-
self-unzip-html encrypted-html -i README.md -o test_download_b85_encrypt.html -e ascii85 -c none -p test --download
38+
self-unzip-html encrypted-html -i README.md -o test_download_b85_encrypt.html -e ascii85 -c none -p test --download-link
3939
self-unzip-html html -i README.md -e base64 -c none --show-text -o test_show.html
4040
self-unzip-html encrypted-html -i README.md -o test_custom.html -e ascii85 -c none -p test --custom 'alert(og_data)'
4141
# Usually you want to redirect to a thank you page, since this legitimizes the downlod you just started

0 commit comments

Comments
 (0)