On 6 January 2024, I posted a decoding challenge to provide a discount code for imposecost.net merchandise. This blog covers the solution. I want to preface that everything is easy once you know how to do it.
![](https://static.wixstatic.com/media/e5f5a7_0795ce3947bf42be9702804ccec1e2ba~mv2.png/v1/fill/w_980,h_868,al_c,q_90,usm_0.66_1.00_0.01,enc_auto/e5f5a7_0795ce3947bf42be9702804ccec1e2ba~mv2.png)
I gave the following block of text:
48 34 73 49 41 4c 53 53 6d 57 55 41 2f 77 33 4c 6a 5a 6e 47 45 41 77 41 34 4a 32 6f 41 66 79 6b 46 45 6b 2b 48 72 32 51 2f 51 66 70 44 66 44 65 54 67 73 63 44 54 44 4f 38 6b 38 48 4a 49 31 52 47 6a 78 30 76 2f 67 61 33 36 67 42 53 70 76 56 4a 63 43 6c 67 65 6e 6b 66 4e 57 41 59 51 71 59 6e 68 6a 47 7a 6d 6f 72 33 43 64 37 71 77 48 72 33 49 38 71 6f 36 72 34 62 6a 4f 36 75 65 4e 6c 43 39 49 73 34 76 52 48 39 43 66 53 4a 78 64 61 41 72 6f 79 6a 79 48 34 37 35 6a 57 35 69 74 35 63 71 5a 45 54 65 75 48 51 34 78 70 2f 41 47 2f 44 75 4b 65 6f 41 41 41 41 41 3d 3d
This block of text can be guessed as being hexadecimal or base16. You expect to see numbers and letters in pairs, and you also expect to see no letter greater than f, as hexadecimal uses the numbers zero through nine and the letters a through f for a total of 16 characters to represent numbers.
When people somewhat familiar with hexadecimal see pairs of numbers and letters in this format, they instantly believe it is hexadecimal, and in this case, it is; however, if you are ever staring at a block of text like this, it doesn't hurt to verify there's no letters greater than 'f', and if there are, it's something other than standard hexadecimal in front of you. Also note that hexadecimal need not be presented to you in neatly spaced pairs that make up a byte, but you should expect to see an even number of numbers and letters, as a byte is represented by two characters.
Since we have what we believe is hexadecimal, we want to see what these bytes represent. One tool I have recommended for decoding triage is CyberChef. We add our block of text into the pane and add the ingredient 'From Hex' to the list of operations we are performing on the block of text.
![](https://static.wixstatic.com/media/e5f5a7_dc6e900369a34e9f9d1d4aebd9377252~mv2.png/v1/fill/w_980,h_1024,al_c,q_90,usm_0.66_1.00_0.01,enc_auto/e5f5a7_dc6e900369a34e9f9d1d4aebd9377252~mv2.png)
H4sIALSSmWUA/w3LjZnGEAwA4J2oAfykFEk+Hr2Q/QfpDfDeTgscDTDO8k8HJI1RGjx0v/ga36gBSpvVJcClgenkfNWAYQqYnhjGzmor3Cd7qwHr3I8qo6r4bjO6ueNlC9Is4vRH9CfSJxdaAroyjyH475jW5it5cqZETeuHQ4xp/AG/DuKeoAAAAA==
We see our hexadecimal sequence of bytes represent ASCII text that you may guess appears to be base64. As explained with base16, base64 encoding uses a 64 character set that includes the full English alphabet lowercase and uppercase, the numbers zero through nine, the addition sign '+', the forward slash '/', and an equal sign '=' for padding, which goes beyond the scope of this blog.
A tell tale sign that people use to visually identify base64 quickly is the padding at the end of the string. People who are used to observing base64 encoded content may quickly recognize the beginning sequence of 'H4sI' as likely to be base64 encoded gzipped data. However, assuming you are not familiar with that, we will use the 'From Base64' ingredient to see what the base64 decodes to.
![](https://static.wixstatic.com/media/e5f5a7_7b06242ea9ca47728ef7f2d4dce8b6d4~mv2.png/v1/fill/w_980,h_1024,al_c,q_90,usm_0.66_1.00_0.01,enc_auto/e5f5a7_7b06242ea9ca47728ef7f2d4dce8b6d4~mv2.png)
We see the output looks like trash. In fact, even to an experienced eye, the base64 encoded gzip looks more identifiable. We are looking at the raw binary content, and that isn't useful. One ingredient you can try is similar to running the 'file' command on a binary from the command line to attempt to identify what the file is. This ingredient is called "Detect File Type" and when we add it, we see it is detected as gzip:
![](https://static.wixstatic.com/media/e5f5a7_ed900c98ec734d0cbf07732ec42a7e74~mv2.png/v1/fill/w_980,h_1024,al_c,q_90,usm_0.66_1.00_0.01,enc_auto/e5f5a7_ed900c98ec734d0cbf07732ec42a7e74~mv2.png)
We want to remove or disable the Detect File Type ingredient now that it has served its purpose, and we want to add the "gunzip" ingredient which will decompress or "inflate" the data. It will reverse the compression applied to the bytes.
![](https://static.wixstatic.com/media/e5f5a7_97b1d2d0370142e6a1fa09b0ef7928c5~mv2.png/v1/fill/w_980,h_1024,al_c,q_90,usm_0.66_1.00_0.01,enc_auto/e5f5a7_97b1d2d0370142e6a1fa09b0ef7928c5~mv2.png)
We get another sequence of text. You may instinctually think this is possibly base64, and if you add the base64 ingredient, you will see the attempted decode; however, that is a dead end. If we examine the string more closely, we find that it does not have tell tale signs of base64.
F5ZHEYZBEQYTAILEMNZCCWKEINFUMU2AKNKEMWKRJ5DEMTZBONYGG4JBMBRWE2LDOEQXGZ3JEFYGA3ZBMJRXIZZOMZZWAL3GM5RXC43HMNRHW5ZPNNVWWLROHNTWEZTGOQQWM3ZBONTXO4DAN52HCZDTPMQW22KO
We notice every letter is capitalized. We notice the use of variance of numbers is more constrained, and we do not see the use of forward slash '/' and '+'. If we use the "Frequency distribution" ingredient, we can get a better idea about the range of characters used.
![](https://static.wixstatic.com/media/e5f5a7_1d97e7b0129a49fca5bb30867cb810ad~mv2.png/v1/fill/w_980,h_1024,al_c,q_90,usm_0.66_1.00_0.01,enc_auto/e5f5a7_1d97e7b0129a49fca5bb30867cb810ad~mv2.png)
We see "Number of bytes represented: 29" and if we scroll down, we see some clues but not definitive evidence of a specific encoding, base32.
---Truncated---
30 0 0%
31 1 0%
32 2 3.13% ||||
33 3 3.13% ||||
34 4 1.88% ||
35 5 3.13% ||||
36 6 0%
37 7 0%
38 8 0%
39 9 0%
---Truncated---
41 A 3.13% ||||
42 B 3.75% ||||
43 C 2.50% |||
44 D 2.50% |||
45 E 6.25% |||||||
46 F 1.88% ||
47 G 3.75% ||||
48 H 3.13% ||||
49 I 1.88% ||
4a J 2.50% |||
4b K 3.13% ||||
4c L 2.50% |||
4d M 6.88% |||||||
4e N 6.25% |||||||
4f O 5% |||||
50 P 1.25% ||
51 Q 3.13% ||||
52 R 3.75% ||||
53 S 0%
54 T 3.75% ||||
55 U 1.25% ||
56 V 0.63% |
57 W 6.25% |||||||
58 X 2.50% |||
59 Y 2.50% |||
5a Z 8.75% |||||||||
Base32 uses all uppercase characters in the English alphabet and the numbers two through seven. We see we're missing some of the characters we would expect for Base32, which are 6, 7 and uppercase S. The other missing characters are not part of the base32 character set. If we add those to the 29 bytes represented, we get 32. Note: like base64, base32 does also incorporate the additional character '=' for padding) We remove or disable the Frequency distribution ingredient and add the "From Base32" ingredient to try it out.
![](https://static.wixstatic.com/media/e5f5a7_ee79397d64624fafb97cf775a35a9aa3~mv2.png/v1/fill/w_980,h_1024,al_c,q_90,usm_0.66_1.00_0.01,enc_auto/e5f5a7_ee79397d64624fafb97cf775a35a9aa3~mv2.png)
/rrc!$10!dcr!YDCKFS@STFYQOFFO!spcq!`cbicq!sgi!p`o!bctg.fs`/fgcqsgcb{w/kkk..;gbfft!fo!sgwp`otqds{!miN
We see the base32 decodes to values that appear to be jibberish but are printable. This text is encoded or encrypted depending on your opinion in modern parlance. Exercises like these generally do not expect you to break computationally expensive encryption, so we're going to want to try things that are commonly employed in exercises such as these. It is very easy for me to point out patterns in this blob of text, because I know what the answer is, but you notice there are characters such as ! that interestingly seem to separate the other text; we will learn these are spaces. We attempt an XOR Brute Force.
![](https://static.wixstatic.com/media/e5f5a7_7d2c315585914c9f820b07ec8182cc37~mv2.png/v1/fill/w_980,h_1024,al_c,q_90,usm_0.66_1.00_0.01,enc_auto/e5f5a7_7d2c315585914c9f820b07ec8182cc37~mv2.png)
We do not see anything human readable right away. We scroll around, but as we scroll, it just gets worse. We return to the top and see some interesting strings.
Key = 01: .ssb %01 ebs XEBJGRARUGXPNGGN rqbp abchbp rfh qan cbuf/gra.gfbprfbczv.jjj//:fcggu gn rfvqanuperz lhO
Things that stand out to us include the period at the beginning of the string, the clean use of spaces between subsequent strings, and we notice one of these strings is all uppercase letters, which is a deviation from the rest of the sequences. Our eye catches "//:fcggu". We know uniform resource locators (URLs) begin with a protocol delimited by a colon and followed by two forward slashes. We see that pair of forward slashes, colon, and some period delimited strings and forward slash.
cbuf/gra.gfbprfbczv.jjj//:fcggu
This clues us in that an XOR using 0x01 is possibly part of our solution, so we remove or disable the XOR Brute Force ingredient and add the XOR ingredient setting the key to 0x01, which is simply entering 01 or 1 into the field and setting it to HEX.
![](https://static.wixstatic.com/media/e5f5a7_3874dfe1da4b4298a4070f49bf5b34c8~mv2.png/v1/fill/w_980,h_1024,al_c,q_90,usm_0.66_1.00_0.01,enc_auto/e5f5a7_3874dfe1da4b4298a4070f49bf5b34c8~mv2.png)
We now have the value we identified through our XOR Brute Force.
.ssb %01 ebs XEBJGRARUGXPNGGN rqbp abchbp rfh qan cbuf/gra.gfbprfbczv.jjj//:fcggu gn rfvqanuperz lhO
We can tell a few things by looking at this. If what we think is a URL is a URL, it is backwards or "reverse" what we would expect, so we add the "Reverse" ingredient.
![](https://static.wixstatic.com/media/e5f5a7_470d1553e2164f9985fdd80b33c6ab39~mv2.png/v1/fill/w_980,h_1024,al_c,q_90,usm_0.66_1.00_0.01,enc_auto/e5f5a7_470d1553e2164f9985fdd80b33c6ab39~mv2.png)
Ohl zrepunaqvfr ng uggcf://jjj.vzcbfrpbfg.arg/fubc naq hfr pbhcba pbqr NGGNPXGURARGJBEX sbe 10% bss.
We see what appears to be a structure of a sentence that includes a URL. We know that our exercise involves winning a 10% discount on merchandise, and we see 10% in our string. We can research different old school cryptography material and learn about Casear Ciphers.
We also learn about a popular implementation known as ROT13, which stands for "Rotate by 13 places". In building this exercise, I came to understand that ROT13 isn't simply a Casear Cipher with 13 as the key; ROT13 only impacts characters within the standard English alphabet.
That's relevant here, because you will note that the structure of the URL is in tact, and other characters outside of the alphabet, such as spaces " ", colons ":", periods "." numbers "10" and percentage sign "%" appear to be unencoded. Furthermore, we can guess that "uggcf" is likely "https" and "jjj" may be "www".
We can either try the "ROT Brute Force" ingredient, or we can try the "ROT13" ingredient right away. While the ROT13 ingredient says "13" you can iterate up and down how many rotation places you wish to perform, which can be useful.
![](https://static.wixstatic.com/media/e5f5a7_39bb20d112c3427fb34c7a5a1cd28190~mv2.png/v1/fill/w_980,h_1024,al_c,q_90,usm_0.66_1.00_0.01,enc_auto/e5f5a7_39bb20d112c3427fb34c7a5a1cd28190~mv2.png)
Buy merchandise at https://www.imposecost.net/shop and use coupon code ATTACKTHENETWORK for 10% off.
We see our plaintext sentence has been revealed, and we're ready to save 10% on our merchandise purchase from imposecost.net!
As a bonus, while I was preparing this write-up, I discovered the CyberChef ingredient "Magic" which is very useful at detecting and recommending solutions. Applying that single ingredient and setting it to four quickly cut through the decoding of the hexadecimal, base64, gzip, and base32.
![](https://static.wixstatic.com/media/e5f5a7_3b62d3595de247c6997975f971425571~mv2.png/v1/fill/w_980,h_1015,al_c,q_90,usm_0.66_1.00_0.01,enc_auto/e5f5a7_3b62d3595de247c6997975f971425571~mv2.png)
Clicking the result applies the applicable ingredients and the user is free to continue the challenge.
![](https://static.wixstatic.com/media/e5f5a7_7d8f6981df714c56bddf1d2725aa5c0f~mv2.png/v1/fill/w_980,h_1015,al_c,q_90,usm_0.66_1.00_0.01,enc_auto/e5f5a7_7d8f6981df714c56bddf1d2725aa5c0f~mv2.png)
Full CyberChef Solution:
Alternative Methods
Binary Refinery
Here is the solution using Binary Refinery, which is an incredible suite of tools similar to CyberChef but for command line, which has major utility:
![](https://static.wixstatic.com/media/e5f5a7_0b25897cfe764c3a82b0fa32960bd1b8~mv2.png/v1/fill/w_980,h_567,al_c,q_90,usm_0.66_1.00_0.01,enc_auto/e5f5a7_0b25897cfe764c3a82b0fa32960bd1b8~mv2.png)
emit "48 34 73 49 41 4c 53 53 6d 57 55 41 2f 77 33 4c 6a 5a 6e 47 45 41 77 41 34 4a 32 6f 41 66 79 6b 46 45 6b 2b 48 72 32 51 2f 51 66 70 44 66 44 65 54 67 73 63 44 54 44 4f 38 6b 38 48 4a 49 31 52 47 6a 78 30 76 2f 67 61 33 36 67 42 53 70 76 56 4a 63 43 6c 67 65 6e 6b 66 4e 57 41 59 51 71 59 6e 68 6a 47 7a 6d 6f 72 33 43 64 37 71 77 48 72 33 49 38 71 6f 36 72 34 62 6a 4f 36 75 65 4e 6c 43 39 49 73 34 76 52 48 39 43 66 53 4a 78 64 61 41 72 6f 79 6a 79 48 34 37 35 6a 57 35 69 74 35 63 71 5a 45 54 65 75 48 51 34 78 70 2f 41 47 2f 44 75 4b 65 6f 41 41 41 41 41 3d 3d" | hex | b64 | zl -g | b32 | rev | xor '0x01' | rot 13
Python
Credit: @vinopaljiri
from malduck import unhex, gzip, xor
from base64 import b64decode, b32decode
from codecs import encode
encode(xor(b'\x01', b32decode(gzip.decompress(b64decode(unhex("48 34 73 49 41 4c 53 53 6d 57 55 41 2f 77 33 4c 6a 5a 6e 47 45 41 77 41 34 4a 32 6f 41 66 79 6b 46 45 6b 2b 48 72 32 51 2f 51 66 70 44 66 44 65 54 67 73 63 44 54 44 4f 38 6b 38 48 4a 49 31 52 47 6a 78 30 76 2f 67 61 33 36 67 42 53 70 76 56 4a 63 43 6c 67 65 6e 6b 66 4e 57 41 59 51 71 59 6e 68 6a 47 7a 6d 6f 72 33 43 64 37 71 77 48 72 33 49 38 71 6f 36 72 34 62 6a 4f 36 75 65 4e 6c 43 39 49 73 34 76 52 48 39 43 66 53 4a 78 64 61 41 72 6f 79 6a 79 48 34 37 35 6a 57 35 69 74 35 63 71 5a 45 54 65 75 48 51 34 78 70 2f 41 47 2f 44 75 4b 65 6f 41 41 41 41 41 3d 3d".replace(" ", ""))))))[::-1].decode(), "rot13")
Decoding and Word Guessing
Credit: @recordedparadox
Comments