Skip to content

Commit f06a128

Browse files
committed
All the improvements, none of the bloat
- Patch DoS vulnerability where Microsoft servers could send us data forever until OOM or disk space fills up. This issue could not feasibly be patched until now: curl/curl#11810 - Improve handle_curl_error - Update win11x64 checksum - Organize assets into their own folder - Add attribution and copyright in download functions - Update copyright year
1 parent cb8fcaf commit f06a128

File tree

8 files changed

+47
-50
lines changed

8 files changed

+47
-50
lines changed

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (C) 2023 Elliot Killick <[email protected]>
3+
Copyright (C) 2024 Elliot Killick <[email protected]>
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

Mido.sh

Lines changed: 38 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/bin/sh
22

3-
# Copyright (C) 2023 Elliot Killick <[email protected]>
3+
# Copyright (C) 2024 Elliot Killick <[email protected]>
44
# Licensed under the MIT License. See LICENSE file for details.
55

66
[ "$DEBUG" ] && set -x
@@ -176,6 +176,12 @@ handle_curl_error() {
176176
7)
177177
echo_err "Failed to contact Microsoft servers! Is there an Internet connection or is the server down?"
178178
;;
179+
8)
180+
echo_err "Microsoft servers returned a malformed HTTP response!"
181+
;;
182+
22)
183+
echo_err "Microsoft servers returned a failing HTTP status code!"
184+
;;
179185
23)
180186
echo_err "Failed at writing Windows media to disk! Out of disk space or permission error? Exiting..."
181187
return "$fatal_error_action"
@@ -187,8 +193,8 @@ handle_curl_error() {
187193
36)
188194
echo_err "Failed to continue earlier download!"
189195
;;
190-
22)
191-
echo_err "Microsoft servers returned failing HTTP status code!"
196+
63)
197+
echo_err "Microsoft servers returned an unexpectedly large response!"
192198
;;
193199
# POSIX defines exit statuses 1-125 as usable by us
194200
# https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_08_02
@@ -235,7 +241,7 @@ scurl_file() {
235241

236242
# --location: Microsoft likes to change which endpoint these downloads are stored on but is usually kind enough to add redirects
237243
# --fail: Return an error on server errors where the HTTP response code is 400 or greater
238-
curl --progress-bar --location --output "$part_file" --continue-at - --fail --proto =https "--tlsv$tls_version" --http1.1 -- "$url" || {
244+
curl --progress-bar --location --output "$part_file" --continue-at - --max-filesize 10G --fail --proto =https "--tlsv$tls_version" --http1.1 -- "$url" || {
239245
error_code=$?
240246
handle_curl_error "$error_code"
241247
error_action=$?
@@ -297,18 +303,13 @@ EOF
297303
}
298304

299305
consumer_download() {
300-
# Download newer consumer Windows versions from behind gated Microsoft API
301-
# This function aims to precisely emulate what Fido does down to the URL requests and HTTP headers (exceptions: updated user agent and referer adapts to Windows version instead of always being "windows11") but written in POSIX sh (with coreutils) and curl instead of PowerShell (also simplified to greatly reduce attack surface)
302-
# However, differences such as the order of HTTP headers and TLS stacks (could be used to do TLS fingerprinting) still exist
306+
# Copyright (C) 2024 Elliot Killick <[email protected]>
307+
# Licensed under the MIT License. See LICENSE file for details.
303308
#
304-
# Command translated: ./Fido -Win 10 -Lang English -Verbose
305-
# "English" = "English (United States)" (as opposed to the default "English (International)")
306-
# For testing Fido, replace all "https://" with "http://" and remove all instances of "-MaximumRedirection 0" (to allow redirection of HTTP traffic to HTTPS) so HTTP requests can easily be inspected in Wireshark
307-
# Fido (command-line only) works under PowerShell for Linux if that makes it easier for you
308-
# UPDATE: Fido v1.4.2+ no longer works without being edited on Linux due to these issues on the Fido GitHub repo (and possibly others after these): #56 and #58
309-
#
310-
# If this function in Mido fails to work for you then please test with the Fido script before creating an issue because we basically just copy what Fido does exactly:
311-
# https://github.com/pbatard/Fido
309+
# This function is from the Mido project:
310+
# https://github.com/ElliotKillick/Mido
311+
312+
# Download newer consumer Windows versions from behind gated Microsoft API
312313

313314
out_file="$1"
314315
# Either 8, 10, or 11
@@ -325,25 +326,23 @@ consumer_download() {
325326

326327
# Get product edition ID for latest release of given Windows version
327328
# Product edition ID: This specifies both the Windows release (e.g. 22H2) and edition ("multi-edition" is default, either Home/Pro/Edu/etc., we select "Pro" in the answer files) in one number
328-
# This is the *only* request we make that Fido doesn't. Fido manually maintains a list of all the Windows release/edition product edition IDs in its script (see: $WindowsVersions array). This is helpful for downloading older releases (e.g. Windows 10 1909, 21H1, etc.) but we always want to get the newest release which is why we get this value dynamically
329+
# This is a request we make that Fido doesn't. Fido manually maintains a list of all the Windows release/edition product edition IDs in its script (see: $WindowsVersions array). This is helpful for downloading older releases (e.g. Windows 10 1909, 21H1, etc.) but we always want to get the newest release which is why we get this value dynamically
329330
# Also, keeping a "$WindowsVersions" array like Fido does would be way too much of a maintenance burden
330-
# Remove "Accept" header that curl sends by default
331-
iso_download_page_html="$(curl --user-agent "$user_agent" --header "Accept:" --fail --proto =https --tlsv1.2 --http1.1 -- "$url")" || {
331+
# Remove "Accept" header that curl sends by default (match Fido requests)
332+
iso_download_page_html="$(curl --user-agent "$user_agent" --header "Accept:" --max-filesize 1M --fail --proto =https --tlsv1.2 --http1.1 -- "$url")" || {
332333
handle_curl_error $?
333334
return $?
334335
}
335336

336-
# Limit untrusted size for input validation
337-
iso_download_page_html="$(echo "$iso_download_page_html" | head -c 102400)"
338337
# tr: Filter for only numerics to prevent HTTP parameter injection
339338
# head -c was recently added to POSIX: https://austingroupbugs.net/view.php?id=407
340339
product_edition_id="$(echo "$iso_download_page_html" | grep -Eo '<option value="[0-9]+">Windows' | cut -d '"' -f 2 | head -n 1 | tr -cd '0-9' | head -c 16)"
341340
[ "$VERBOSE" ] && echo "Product edition ID: $product_edition_id" >&2
342341

343342
# Permit Session ID
344343
# "org_id" is always the same value
345-
curl --output /dev/null --user-agent "$user_agent" --header "Accept:" --fail --proto =https --tlsv1.2 --http1.1 -- "https://vlscppe.microsoft.com/tags?org_id=y6jn8c31&session_id=$session_id" || {
346-
# This should only happen if there's been some change to how this API works (copy whatever fix Fido implements)
344+
curl --output /dev/null --user-agent "$user_agent" --header "Accept:" --max-filesize 100K --fail --proto =https --tlsv1.2 --http1.1 -- "https://vlscppe.microsoft.com/tags?org_id=y6jn8c31&session_id=$session_id" || {
345+
# This should only happen if there's been some change to how this API works
347346
handle_curl_error $?
348347
return $?
349348
}
@@ -355,29 +354,24 @@ consumer_download() {
355354
# SKU ID: This specifies the language of the ISO. We always use "English (United States)", however, the SKU for this changes with each Windows release
356355
# We must make this request so our next one will be allowed
357356
# --data "" is required otherwise no "Content-Length" header will be sent causing HTTP response "411 Length Required"
358-
language_skuid_table_html="$(curl --request POST --user-agent "$user_agent" --data "" --header "Accept:" --fail --proto =https --tlsv1.2 --http1.1 -- "https://www.microsoft.com/en-US/api/controls/contentinclude/html?pageId=a8f8f489-4c7f-463a-9ca6-5cff94d8d041&host=www.microsoft.com&segments=software-download,$url_segment_parameter&query=&action=getskuinformationbyproductedition&sessionId=$session_id&productEditionId=$product_edition_id&sdVersion=2")" || {
357+
language_skuid_table_html="$(curl --request POST --user-agent "$user_agent" --data "" --header "Accept:" --max-filesize 10K --fail --proto =https --tlsv1.2 --http1.1 -- "https://www.microsoft.com/en-US/api/controls/contentinclude/html?pageId=a8f8f489-4c7f-463a-9ca6-5cff94d8d041&host=www.microsoft.com&segments=software-download,$url_segment_parameter&query=&action=getskuinformationbyproductedition&sessionId=$session_id&productEditionId=$product_edition_id&sdVersion=2")" || {
359358
handle_curl_error $?
360359
return $?
361360
}
362361

363-
# Limit untrusted size for input validation
364-
language_skuid_table_html="$(echo "$language_skuid_table_html" | head -c 10240)"
365362
# tr: Filter for only alphanumerics or "-" to prevent HTTP parameter injection
366363
sku_id="$(echo "$language_skuid_table_html" | grep "English (United States)" | sed 's/&quot;//g' | cut -d ',' -f 1 | cut -d ':' -f 2 | tr -cd '[:alnum:]-' | head -c 16)"
367364
[ "$VERBOSE" ] && echo "SKU ID: $sku_id" >&2
368365

369366
# Get ISO download link
370367
# If any request is going to be blocked by Microsoft it's always this last one (the previous requests always seem to succeed)
371368
# --referer: Required by Microsoft servers to allow request
372-
iso_download_link_html="$(curl --request POST --user-agent "$user_agent" --data "" --referer "$url" --header "Accept:" --fail --proto =https --tlsv1.2 --http1.1 -- "https://www.microsoft.com/en-US/api/controls/contentinclude/html?pageId=6e2a1789-ef16-4f27-a296-74ef7ef5d96b&host=www.microsoft.com&segments=software-download,$url_segment_parameter&query=&action=GetProductDownloadLinksBySku&sessionId=$session_id&skuId=$sku_id&language=English&sdVersion=2")" || {
369+
iso_download_link_html="$(curl --request POST --user-agent "$user_agent" --data "" --referer "$url" --header "Accept:" --max-filesize 100K --fail --proto =https --tlsv1.2 --http1.1 -- "https://www.microsoft.com/en-US/api/controls/contentinclude/html?pageId=6e2a1789-ef16-4f27-a296-74ef7ef5d96b&host=www.microsoft.com&segments=software-download,$url_segment_parameter&query=&action=GetProductDownloadLinksBySku&sessionId=$session_id&skuId=$sku_id&language=English&sdVersion=2")" || {
373370
# This should only happen if there's been some change to how this API works
374371
handle_curl_error $?
375372
return $?
376373
}
377374

378-
# Limit untrusted size for input validation
379-
iso_download_link_html="$(echo "$iso_download_link_html" | head -c 4096)"
380-
381375
if ! [ "$iso_download_link_html" ]; then
382376
# This should only happen if there's been some change to how this API works
383377
echo_err "Microsoft servers gave us an empty response to our request for an automated download. Please manually download this ISO in a web browser: $url"
@@ -410,6 +404,12 @@ consumer_download() {
410404
}
411405

412406
enterprise_eval_download() {
407+
# Copyright (C) 2024 Elliot Killick <[email protected]>
408+
# Licensed under the MIT License. See LICENSE file for details.
409+
#
410+
# This function is from the Mido project:
411+
# https://github.com/ElliotKillick/Mido
412+
413413
# Download enterprise evaluation Windows versions
414414

415415
out_file="$1"
@@ -418,14 +418,11 @@ enterprise_eval_download() {
418418

419419
url="https://www.microsoft.com/en-us/evalcenter/download-$windows_version"
420420

421-
iso_download_page_html="$(curl --location --fail --proto =https --tlsv1.2 --http1.1 -- "$url")" || {
421+
iso_download_page_html="$(curl --location --max-filesize 1M --fail --proto =https --tlsv1.2 --http1.1 -- "$url")" || {
422422
handle_curl_error $?
423423
return $?
424424
}
425425

426-
# Limit untrusted size for input validation
427-
iso_download_page_html="$(echo "$iso_download_page_html" | head -c 102400)"
428-
429426
if ! [ "$iso_download_page_html" ]; then
430427
# This should only happen if there's been some change to where this download page is located
431428
echo_err "Windows enterprise evaluation download page gave us an empty response"
@@ -450,12 +447,17 @@ enterprise_eval_download() {
450447
esac
451448

452449
# Follow redirect so proceeding log message is useful
450+
# This is a request we make this Fido doesn't
451+
# We don't need to set "--max-filesize" here because this is a HEAD request and the output is to /dev/null anyway
453452
iso_download_link="$(curl --location --output /dev/null --silent --write-out "%{url_effective}" --head --fail --proto =https --tlsv1.2 --http1.1 -- "$iso_download_link")" || {
454453
# This should only happen if the Microsoft servers are down
455454
handle_curl_error $?
456455
return $?
457456
}
458457

458+
# Limit untrusted size for input validation
459+
iso_download_link="$(echo "$iso_download_link" | head -c 1024)"
460+
459461
echo_ok "Got latest ISO download link: $iso_download_link"
460462

461463
# Use highest TLS version for endpoints that support it
@@ -473,11 +475,6 @@ download_media() {
473475

474476
media_download_failed_list=""
475477

476-
# Always use HTTP/1.1 (--http1.1) because HTTP/2 and HTTP/3 have proven too buggy in curl:
477-
# https://github.com/curl/curl/issues?q=is%3Aissue+label%3Acrash
478-
# HTTP 2 and 3 don't provide a performance benefit for downloading large files anyway (tested)
479-
# It's a free security enchancement
480-
481478
for media in $media_list; do
482479
case "$media" in
483480
"$win7x64_ultimate")
@@ -583,8 +580,8 @@ dec04cbd352b453e437b2fe9614b67f28f7c0b550d8351827bc1e9ef3f601389 win7x64-ultima
583580
d8333cf427eb3318ff6ab755eb1dd9d433f0e2ae43745312c1cd23e83ca1ce51 win81x64.iso
584581
# Windows 10 22H2
585582
a6f470ca6d331eb353b815c043e327a347f594f37ff525f17764738fe812852e win10x64.iso
586-
# Windows 11 23H2
587-
71a7ae6974866603d366a911b0c00eace476e0b49d12205d7529765cc50b4b39 win11x64.iso
583+
# Windows 11 23H2 v2
584+
36de5ecb7a0daa58dce68c03b9465a543ed0f5498aa8ae60ab45fb7c8c4ae402 win11x64.iso
588585
2dedd44c45646c74efc5a028f65336027e14a56f76686a4631cf94ffe37c72f2 win81x64-enterprise-eval.iso
589586
ef7312733a9f5d7d51cfa04ac497671995674ca5e1058d5164d6028f0938d668 win10x64-enterprise-eval.iso
590587
ebbc79106715f44f5020f77bd90721b17c5a877cbc15a3535b99155493a1bb3f win11x64-enterprise-eval.iso
@@ -695,7 +692,7 @@ ending_summary() {
695692
# 2: Runtime error (see error message for more info)
696693
# 3: One or more downloads failed
697694
# 4: One or more verifications failed
698-
# 5: At least one download and one verification failed (when more than one media specified)
695+
# 5: At least one download and one verification failed (when more than one media is specified)
699696

700697
exit_code=0
701698

README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<div align="center">
22
<a href="https://github.com/ElliotKillick/Mido">
3-
<img width="160" src="logo.png" alt="Logo" />
3+
<img width="160" src="assets/logo.png" alt="Logo" />
44
</a>
55
</div>
66

@@ -21,19 +21,19 @@ It's very well-suited to full automation if you just want to set it and forget i
2121
#### ❌ Microsoft's Media Creation Tool (`mediacreationtool.exe` proprietary bloatware)
2222

2323
<p align="center">
24-
<img src="bloatware.png" alt="Microsoft's proprietary bloatware executable"></img>
24+
<img src="assets/bloatware1.png" alt="Microsoft's proprietary bloatware executable"></img>
2525
<br />
26-
<img src="bloatware2.png" width="250px" alt="Microsoft's proprietary bloatware"></img>
27-
<img src="bloatware3.png" width="200px" alt="Microsoft's bloatware"></img>
26+
<img src="assets/bloatware2.png" width="250px" alt="Microsoft's proprietary bloatware"></img>
27+
<img src="assets/bloatware3.png" width="200px" alt="Microsoft's bloatware"></img>
2828
</p>
2929

3030
Bloated website: `https://www.microsoft.com/en-us/software-download/windows11`
3131
- Mido provides the exact same downloads as this website (it uses the same API)
3232

33-
#### ✔️ Mido (using the **same** official Microsoft servers; <img src="https://awesome.re/badge.svg" style="position: relative; top: 5px;"></img> open source software)
33+
#### Mido (using the **same** official Microsoft servers; <img src="https://awesome.re/badge.svg" style="position: relative; top: 5px;"></img> open source software)
3434

3535
<p align="center">
36-
<img src="demo.gif" width="400" alt="Project demo GIF"></img>
36+
<img src="assets/demo.gif" width="400" alt="Project demo GIF"></img>
3737
</p>
3838

3939
## Get Mido
@@ -73,7 +73,7 @@ Check out the `create-media.sh` script in [Qvm-Create-Windows-Qube](https://gith
7373

7474
## How secure is it *really*?
7575

76-
Mido is very secure. Every chance to reduce attack surface is taken. Untrusted data is treated as such with proper validation steps. The highest possible version of TLS is always used (up to TLS 1.3). Easily verify security properties yourself in the transparent shell script.
76+
Mido is super secure. Every chance to reduce attack surface is taken. Untrusted data is treated as such with proper validation steps. The highest possible version of TLS is always used (up to TLS 1.3). Easily verify security properties yourself in the transparent shell script.
7777

7878
No web browser (e.g. headless Chromium running JavaScript) reduces the attack surface by *many* orders of magnitude.
7979

@@ -84,7 +84,7 @@ The next [Shellshock/Bashdoor](https://en.wikipedia.org/wiki/Shellshock_(softwar
8484
- For even *greater* security, one could use a POSIX-compliant Rust shell (e.g. nsh) with Rust coreutils (e.g. uutils). This is not the default configuration.
8585

8686
Frequent [Curl HTTP 2.0 & 3.0 bugs](https://github.com/curl/curl/issues?q=is%3Aissue+label%3Acrash)? Force HTTP/1.1.
87-
- Comes at zero cost to performance for downloading files
87+
- Comes at zero cost to performance for downloading a single large file
8888

8989
Coreutil bugs? Only builtins are used for the most critical functionality.
9090

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)