You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardexpand all lines: solve/decrypt_eval/index.md
+256-1
Original file line number
Diff line number
Diff line change
@@ -17,8 +17,29 @@ Solved: 197
17
17
18
18
Input files:
19
19
20
-
??? info "encoding.txt"
20
+
??? info "decrypt-then-eval.py"
21
+
```py
22
+
#!/usr/bin/env python3
21
23
24
+
from Crypto.Cipher import AES
25
+
import os
26
+
27
+
KEY = os.urandom(16)
28
+
IV = os.urandom(16)
29
+
FLAG = os.getenv('FLAG', 'DUCTF{testflag}')
30
+
31
+
def main():
32
+
while True:
33
+
ct = bytes.fromhex(input('ct: '))
34
+
aes = AES.new(KEY, AES.MODE_CFB, IV, segment_size=128)
35
+
try:
36
+
print(eval(aes.decrypt(ct)))
37
+
except Exception:
38
+
print('invalid ct!')
39
+
40
+
if __name__ == '__main__':
41
+
main()
42
+
```
22
43
23
44
NB:
24
45
@@ -32,12 +53,246 @@ NB:
32
53
Ie, first character of string `Hello World!` is `H`, fifth is `o`.
33
54
34
55
* Solution code was redacted for readability purposes. Due to time pressure during the competition I was using a lot of one-letter variables and questionable code structure.
56
+
57
+
* I am using gdb with [pwndbg](https://github.com/pwndbg/pwndbg) plugin
35
58
36
59
## My struggle
37
60
61
+
### Analysis
62
+
63
+
We got only one file to start with:
64
+
65
+
```py
66
+
KEY= os.urandom(16) # AES params
67
+
IV= os.urandom(16)
68
+
FLAG= os.getenv('FLAG', 'DUCTF{testflag}') # flag variable this will be our target
aes =AES.new(KEY, AES.MODE_CFB, IV, segment_size=128) # create AES cipher
74
+
try:
75
+
print(eval(aes.decrypt(ct))) # decrypt input string, evaluate result value
76
+
exceptException:
77
+
print('invalid ct!')
78
+
```
79
+
Our goal is to get `aes.decrypt` return string `FLAG`, then evaluation of it will print value of the
80
+
`FLAG` variable back to us.
81
+
82
+
AES is considered to be a secure algorithm. If its used correctly - its practically unbreakable. The key part of
83
+
this statement is "if used correctly". The key issue of the implementation is that it `AES.new` is created afresh for every
84
+
user input. Given IV and KEY are same every time, same cipher keystream is generated for each input. Combined with the fact
85
+
that CFB mode is used, we can control result of decryption even though we will never know values of KEY and IV.
86
+
87
+
Lets review strategy of controlling output of AES description in CFB mode when same keystream is applied to every input
88
+
that we provide. Program executes following algorithm:
89
+
90
+
1. Read used input;
91
+
2. Generate same keystream every time for given KEY nad IV
92
+
3. XOR input with keystream
93
+
4. Evaluate result
94
+
5. If expression is evaluated successfully - print result, otherwise print "invalid ct!"
95
+
96
+
If we know the keystream, we can easily construct input that will give us any desired output. For example if first keystream byte
97
+
was 0x67 and we would want it to be 'F' (ascii value 0x46) then input we are lookign for is `0x67 ^ 0x46 = 0x21`. Same calculation
98
+
works for any other byte value of the keystream.
99
+
100
+
### Attempt 1
101
+
102
+
How would we find the keystream? My first idea was to loop through all possible inputs until I get first and last characters
103
+
to be double quotes, then everything is the middle will be considered as string that will be printed back.
104
+
Once I have bytes the middle, I can calculate input.
105
+
106
+
Pseudocode that I used for this:
107
+
108
+
```py title="attempt_1.py"
109
+
input= [0] *16# this is our input 16 bytes long (source code mentioned segment size 128)
110
+
# when same byte of keystream is XOR-ed with 256 different values in input
111
+
# output will also cover 256 possible values
112
+
# one of them will be double quote that I am looking for
113
+
for i inrange(256): # try all possible first bytes
114
+
for j inrange(256): # try all possible last bytes
115
+
input[0] = i # set first byte to i, last byte to j, all others will be 0
116
+
input[15] = j
117
+
io.sendline(binascii.hexlify(bytearray(input))) # send input to the decrypt-eval program
118
+
response = io.recvline().strip() # read result line
119
+
# if we got something interesting - print it, I expect to double quoted string and single quoted
120
+
# and maybe some other inputs that are randomly valid
121
+
ifb'invlaid ct!'notin line:
122
+
print("We received response that is not error: ", line)
123
+
```
124
+
125
+
I've run the program and to my surprise I got nothing. There must be some other evaluation errors. I've modified source code of the decrypt-eval program
126
+
to include more debug information printed while keeping functionality intact and increasing
127
+
performance. With this version I can iterate much quicker:
128
+
129
+
```py title="modified decrypt-then-eval.py"
130
+
aes =AES.new(KEY, AES.MODE_CFB, IV, segment_size=128) # create AES instance at the start of the program
131
+
keysream = aes.decrypt(bytearray(16)) # by using input [0,0,0,0....0] extract keystream
132
+
133
+
defmain():
134
+
whileTrue:
135
+
ct =bytes.fromhex(input('ct: '))
136
+
try:
137
+
print(eval(xor_arrays(keysream, ct))) # xor keystream with provided input
138
+
exceptExceptionas e:
139
+
print('invalid ct!', e) # add exception details to the message
140
+
```
141
+
Once I rerun my enumeration script I found errors of the eval:
142
+
143
+
1. source code string cannot contain null bytes;
144
+
2. invalid utf8 encoding
145
+
146
+
Looks like there is a bad sequence of bytes somewhere in the middle of the string. So far, all input middle bytes were 0. I think
147
+
we should try different value to deal with encoding problems. For nullbyte error we should try both 0 and 1 as input
148
+
(only one may produce null-byte, not both at the same time).
149
+
150
+
Pair 127,128 should take care of invalid UTF-8 sequence. UTF8 encoded characters are variable length byte sequences.
151
+
It means that frequently used characters like latin alphabet, digits will take only 1 byte, and some less frequently used (emoji etc)
152
+
assign 2-3 byte sequences. Decoding process is quite straightforward: first bit has a special meaning, its a flag indicating
153
+
that current byte is final byte of codepoint. Remaining bits are concatenated to form codepoint value. For example:
154
+
```
155
+
0XXXXXXX -> 1 byte sequence, codepoint is XXXXXXX
156
+
1AAAAAAA 0BBBBBBB -> 2 byte sequence codepoints is AAAAAAABBBBBBB
157
+
1AAAAAAA 1BBBBBBB 0CCCCCCC -> 3 byte sequence codepoint is AAAAAAABBBBBBBCCCCCCC
158
+
...
159
+
```
160
+
1 byte UTF-8 symbols are all defined (matches ascii table for backwards compatibility), 2+ byte sequences have gaps and not
161
+
every codepoint is defined (ie valid). This is where we get encoding errors. If all characters were 1byte sequences, we would
162
+
not have undefined codepoints error. Pair `127, 128` should take care of it - flips highes bit, therefore we can
163
+
reach a sequence in output where every byte is starting with 0 and is not a null-byte.
164
+
165
+
The resulting candidate values to try for input bytes 1..14 are `[0, 1, 127, 128]`.
166
+
167
+
### Attempt 2
38
168
169
+
Here is script to enumerate through all values 0..255 for bytes 0 and 15, and vocabulary [0, 1, 127, 128]
170
+
for bytes 1..14 as discussed earlier to deal with encoding errors.
171
+
172
+
Feel free to skip implementation of the function `input_generator` as long as you understand the sequence that its producing and
173
+
reasoning why we want this sequence (I think implementation is not too important for understanding the challenge).
174
+
Loop in the end of the snippet is mostly the same as before.
Copy file name to clipboardexpand all lines: solve/dna/index.md
+2
Original file line number
Diff line number
Diff line change
@@ -33,6 +33,8 @@ NB:
33
33
34
34
* Solution code was redacted for readability purposes. Due to time pressure during the competition I was using a lot of one-letter variables and questionable code structure.
35
35
36
+
* I am using gdb with [pwndbg](https://github.com/pwndbg/pwndbg) plugin
Copy file name to clipboardexpand all lines: solve/jmp_flag/index.md
+2
Original file line number
Diff line number
Diff line change
@@ -33,6 +33,8 @@ NB:
33
33
34
34
* Solution code was redacted for readability purposes. Due to time pressure during the competition I was using a lot of one-letter variables and questionable code structure.
35
35
36
+
* I am using gdb with [pwndbg](https://github.com/pwndbg/pwndbg) plugin
Copy file name to clipboardexpand all lines: solve/number_mashing/index.md
+2
Original file line number
Diff line number
Diff line change
@@ -34,6 +34,8 @@ NB:
34
34
35
35
* Solution code was redacted for readability purposes. Due to time pressure during the competition I was using a lot of one-letter variables and questionable code structure.
36
36
37
+
* I am using gdb with [pwndbg](https://github.com/pwndbg/pwndbg) plugin
Copy file name to clipboardexpand all lines: solve/pac_shell/index.md
+2
Original file line number
Diff line number
Diff line change
@@ -33,6 +33,8 @@ NB:
33
33
34
34
* Solution code was redacted for readability purposes. Due to time pressure during the competition I was using a lot of one-letter variables and questionable code structure.
35
35
36
+
* I am using gdb with [pwndbg](https://github.com/pwndbg/pwndbg) plugin
Copy file name to clipboardexpand all lines: solve/rusty/index.md
+2
Original file line number
Diff line number
Diff line change
@@ -33,6 +33,8 @@ NB:
33
33
34
34
* Solution code was redacted for readability purposes. Due to time pressure during the competition I was using a lot of one-letter variables and questionable code structure.
35
35
36
+
* I am using gdb with [pwndbg](https://github.com/pwndbg/pwndbg) plugin
Copy file name to clipboardexpand all lines: solve/shufflebox/index.md
+2
Original file line number
Diff line number
Diff line change
@@ -57,6 +57,8 @@ NB:
57
57
58
58
* Solution code was redacted for readability purposes. Due to time pressure during the competition I was using a lot of one-letter variables and questionable code structure.
59
59
60
+
* I am using gdb with [pwndbg](https://github.com/pwndbg/pwndbg) plugin
61
+
60
62
## My struggle
61
63
62
64
First things first - review source code of the script that ciphers data. Explanation for relevant parts of the code added as comments:
Copy file name to clipboardexpand all lines: solve/sign_in/index.md
+2
Original file line number
Diff line number
Diff line change
@@ -33,6 +33,8 @@ NB:
33
33
34
34
* Solution code was redacted for readability purposes. Due to time pressure during the competition I was using a lot of one-letter variables and questionable code structure.
35
35
36
+
* I am using gdb with [pwndbg](https://github.com/pwndbg/pwndbg) plugin
Copy file name to clipboardexpand all lines: solve/sssshhhh/index.md
+2
Original file line number
Diff line number
Diff line change
@@ -33,6 +33,8 @@ NB:
33
33
34
34
* Solution code was redacted for readability purposes. Due to time pressure during the competition I was using a lot of one-letter variables and questionable code structure.
35
35
36
+
* I am using gdb with [pwndbg](https://github.com/pwndbg/pwndbg) plugin
Copy file name to clipboardexpand all lines: solve/transmissions/index.md
+2
Original file line number
Diff line number
Diff line change
@@ -38,6 +38,8 @@ NB:
38
38
39
39
* Solution code was redacted for readability purposes. Due to time pressure during the competition I was using a lot of one-letter variables and questionable code structure.
40
40
41
+
* I am using gdb with [pwndbg](https://github.com/pwndbg/pwndbg) plugin
42
+
41
43
## My struggle
42
44
43
45
Quick google CCIR476 leads us to wiki page that explains that CCIR476 is a character enconding used in radio data protocol.
Copy file name to clipboardexpand all lines: solve/vector/index.md
+1
Original file line number
Diff line number
Diff line change
@@ -70,6 +70,7 @@ NB:
70
70
Ie, first character of string `Hello World!` is `H`, fifth is `o`.
71
71
72
72
* Solution code was redacted for readability purposes. Due to time pressure during the competition I was using a lot of one-letter variables and questionable code structure.
73
+
73
74
* I am using gdb with [pwndbg](https://github.com/pwndbg/pwndbg) plugin
Copy file name to clipboardexpand all lines: solve/yawa/index.md
+2
Original file line number
Diff line number
Diff line change
@@ -33,6 +33,8 @@ NB:
33
33
34
34
* Solution code was redacted for readability purposes. Due to time pressure during the competition I was using a lot of one-letter variables and questionable code structure.
35
35
36
+
* I am using gdb with [pwndbg](https://github.com/pwndbg/pwndbg) plugin
0 commit comments