Basic knowledge
CBC flip attack
Introduction:
When one of our values C is obtained by the exclusive or of A and B
C = A XOR B
that
A XOR B XOR C is obviously = 0
When we know B and C, it's easy to get the value of A
A = B XOR C
Therefore, A XOR B XOR C is equal to 0. With this formula, we can set our own value at the end of XOR operation to change it.
Cbc-aes: encrypt (plaintext ^ IV)
For example, the CBC first block (16 bits) is known
- Plaintext
- ciphertext
Want to directly modify the 11th character 0 - > 1
Given that the 11th character is 1, we can directly encrypt the encrypted character
Ciphertext [10] = ciphertext [10] xor '0' xor '1'
Can be modified
BASE64 basic encryption principle
A=>QQ==
a=>YQ==
97 = 110 0001 = > shift 2 bits right to 6 bits = > 1 1000 = = > look up table (24)Y
The last remaining 01 binary shifts four bits to the right 010000 = > look up table (16)Q
Fill (= =)
Result YQ==
- In BASE64, it is usually encoded as a group starting with 3 characters (ASCII)
- In the conversion to binary, it will be grouped in 6-bit binary (i.e. a group: 4 * 6 = 24)
- In empty data, use = as the padding character
a | |||
---|---|---|---|
1 1000 | 010000 | ||
Y | Q | = | = |
SpecialLCG
analysis
MSSCTF 2021 preliminary question learning
note
from Crypto.Util.number.inverse = gmpy2.invert inverse
Bin (numeric value), the binary value of the numeric value can be obtained
- Don't think about learning and using now. In the long run, understand slowly and don't be persistent
EXP
from Crypto.Util.number import * n=18253588106473969889 data=[8331802587873314500,16970700310063771377,16378474859328460142,13073117282614811463,747433301416436433] t=[] for i in range(4): t.append(data[i+1]-data[i]) a1=(t[2]*inverse(t[0],n)-t[3]*inverse(t[1],n))*inverse((t[1]*inverse(t[0],n)-t[2]*inverse(t[1],n)),n)%n b1=(t[3]-a1*t[2])*inverse(t[1],n)%n c1=(data[2]-data[1]*a1-data[0]*b1)%n print(long_to_bytes(a1)+long_to_bytes(b1)+long_to_bytes(c1))
babyLCG
analysis
a. If b and m are known, LCG can be solved by finding seed
Linear recursive expression:
self._state = (self._key['a'] * self._state + self._key['b']) % self._key['m']
143893630627599013207723094044959571968= (107763262682494809191803026213015101802*x1+153582801876235638173762045261195852087)%226649634126248141841388712969771891297
Follow the topic to understand (LCG ALGORITHM = = > Euclidean expansion algorithm)
https://blog.csdn.net/superprintf/article/details/108964563
According to the formula, restore seed (calculate the inverse element of a and N, and then set the formula to calculate seed)
Formula for restoring seed: seed = (ani*(seed-b))%n
a=107763262682494809191803026213015101802 n=226649634126248141841388712969771891297 b = 153582801876235638173762045261195852087 c =11267068470666042741<<64#old str ani=invert(a,n)# seed=c print(ani) print("seed:\n") seed = (ani*(seed-b))%n#formula print(seed)# Seed: 222435278211805578675570877319055662119
The first seed can be solved. What is the relationship between related seeds? (the next seed has nothing to do with the previous seed)
[ACTF freshman competition 2020] crypto AES
Title Description
from Cryptodome.Cipher import AES import os import gmpy2 from flag import FLAG from Cryptodome.Util.number import * def main(): key=os.urandom(2)*16 iv=os.urandom(16) print(bytes_to_long(key)^bytes_to_long(iv)) aes=AES.new(key,AES.MODE_CBC,iv) enc_flag = aes.encrypt(FLAG) print(enc_flag) if __name__=="__main__": main() 1144196586662942563895769614300232343026691029427747065707381728622849079757 b'\x8c-\xcd\xde\xa7\xe9\x7f.b\x8aKs\xf1\xba\xc75\xc4d\x13\x07\xac\xa4&\xd6\x91\xfe\xf3\x14\x10|\xf8p'
analysis
Be bold to guess and try
xor is a reducible algorithm
a^b=c c^a=b
CBC feature: IV = the previous ciphertext, and the plaintext needs XOR IV before encryption
EXP
from Crypto.Cipher import AES import os import gmpy2 #from flag import FLAG from Crypto.Util.number import * XOR = lambda s1 , s2 : bytes([x1^x2 for x1,x2 in zip(s1,s2)]) def main(): flag="flag{xxx}" oldxorcipher=b'\xc9\x81\xc9\x81\xc9\x81\xc9\x81\xc9\x81\xc9\x81\xc9\x81\xc9\x81N\xed\x98\xe3\x80\xb15gc\x84\x990\xc8P\xb9\xcd' #The 16 bit plaintext key is leaked, and the last 16 xor is the reducible algorithm xor cipher=b'\x8c-\xcd\xde\xa7\xe9\x7f.b\x8aKs\xf1\xba\xc75\xc4d\x13\x07\xac\xa4&\xd6\x91\xfe\xf3\x14\x10|\xf8p' iv=b'\x87lQbI0\xfc\xe6\xaa\x05P\xb1\x01\xd1pL'#long_to_bytes(bytes_to_long(key[:16])^bytes_to_long(oldxorcipher[16:])) key=b"\xc9\x81"*16 aes=AES.new(key,AES.MODE_CBC,iv) ans=aes.decrypt(cipher[:16]) print(ans)#pre 16:actf{W0W_y0u_can aes=AES.new(key,AES.MODE_CBC,cipher[:16])#CBC feature IV = previous ciphertext ans=aes.decrypt(cipher[16:]) print(ans)#last 16:_so1v3_AES_now!} #actf{W0W_y0u_can_so1v3_AES_now!} if __name__=="__main__": main() # 91144196586662942563895769614300232343026691029427747065707381728622849079757 # b'\x8c-\xcd\xde\xa7\xe9\x7f.b\x8aKs\xf1\xba\xc75\xc4d\x13\x07\xac\xa4&\xd6\x91\xfe\xf3\x14\x10|\xf8p'
StandardCBC
Title Description
from gmssl import sm4 #https://github.com/duanhongyi/gmssl import socketserver import signal from flag import flag import os from base64 import * import random menu = '''1.enc; 2.dec; 3.getflag; ''' XOR = lambda s1 , s2 : bytes([x1^x2 for x1,x2 in zip(s1,s2)]) def pad(m): padlen = 16 - len(m) % 16 return m + padlen * bytes([padlen]) def unpad(m): return m[:-m[-1]] def enc(iv , m , key): enc = sm4.CryptSM4(mode=sm4.SM4_ENCRYPT) enc.set_key(key = key , mode = sm4.SM4_ENCRYPT) c = enc.crypt_cbc(iv, m) return iv + c def dec(iv , c , key): dec = sm4.CryptSM4(mode=sm4.SM4_DECRYPT) dec.set_key(key = key , mode = sm4.SM4_DECRYPT) m = dec.crypt_cbc(iv, c) return m class server(socketserver.BaseRequestHandler): def _recv(self): data = self.request.recv(1024) return data.strip() def _send(self, msg, newline=True): if isinstance(msg , bytes): msg += b'\n' else: msg += '\n' msg = msg.encode() self.request.sendall(msg) def handle(self): signal.alarm(600) key = os.urandom(16) secret = os.urandom(random.randint(16 , 31)) while 1: try: iv = os.urandom(16) self._send(menu) choice = self._recv() if choice == b'1': self._send(b'your message:') msg = b64decode(self._recv()) self._send(b64encode(enc(iv , msg + secret , key))) elif choice == b'2': self._send('your ciphertext:') c = b64decode(self._recv()) self._send('your iv:') iv = b64decode(self._recv()) self._send(b64encode(dec(iv , c , key))[-1:]) elif choice == b'3': self._send('do you know my secret?') guess = b64decode(self._recv()) if guess == secret: self._send('congratulations') self._send(flag) else: self._send('I know you can\'t know it') break else: self._send('wrong!') break except: pass class ForkedServer(socketserver.ForkingMixIn, socketserver.TCPServer): pass if __name__ == "__main__": HOST, PORT = '0.0.0.0', 10001 server = ForkedServer((HOST, PORT), server) server.allow_reuse_address = True server.serve_forever()
analysis
Title of MINIL
AES (plaintext ^ iv)=cipher
The second chunk enc (plaintext ^ previous ciphertext)
There is no doubt that the program logic needs to be exploded, and dec only returns the last digit
Please see the overall logic of the program before writing the code~
Don't [[oversimplify]], and don't think too complicated
It's over!
fact:
- IV it's different every time
- Decryption only returns the last 1-bit base64 encoded character
It's a little difficult. Give up temporarily
EXP
EXP chose the idea of brute force cracking
From XDSEC GITHUB:https://github.com/XDSEC/miniLCTF_2021/blob/main/WriteUps/H4n53r/H4n53r-TEAM.md
from pwn import * from base64 import b64decode, b64encode from Crypto.Util.number import long_to_bytes def get_least_length(): for i in range(1, 16): guess = b'\x00' * i c = b64decode(get_recv(guess)) if i == 1: base = len(c) if len(c) != base: return base - 16 - i def get_recv(x): io.send(b'1') io.recvuntil(b':') io.send(b64encode(x)) Res = io.recvuntil(b'flag;').decode().split('\n') return Res[1] def get_message_last(c): guess = long_to_bytes(66) * 239 for i in range(256): G = guess + long_to_bytes(i) io.send(b'2') io.recvuntil(b':') io.send(b64encode(G + c)) io.recvuntil(b':') io.send(b64encode(IV)) resp = io.recvuntil('flag;').decode().split('\n')[1] if resp == '': return i if __name__ == "__main__": IV = b'\x00'*16 LengTh = 0 ciphertext = [] M = [0]*17 while LengTh == None or LengTh != 17: try: io = remote('0.0.0.0', 10001) io.recv() LengTh = get_least_length() print(LengTh) except: io.close() print('Get Length!!!') for i in range(16): pad = b'\x76' * 16 + (15 - i) * b'\x00' res = get_recv(pad) ciphertext.append(b64decode(res)) print('Get Ciphertext!!!') i = 0 for c in ciphertext: print(i) if i == 0: c16 = c[48:64] M[-1] = long_to_bytes(get_message_last(c16) ^ c[47]) c16 = c[32:48] M[i] = long_to_bytes(get_message_last(c16) ^ c[31]) i += 1 m = b''.join(M) print('Get Message!!!') io.recv() io.send(b'3') print(io.recv()) io.send(b64encode(m)) print(io.recv()) print(io.recv())
Additional knowledge
[[Padding-Oracle]]
Therefore, the key concept behind Padding Oracle Attack is Padding during encryption / decryption. Plaintext information can be of any length, but block encryption algorithm requires that all information be composed of a certain number of data blocks. In order to meet this demand, the plaintext needs to be filled, so that it can be divided into complete data blocks.
A variety of filling rules can be used for encryption, but one of the most common filling methods is the rules defined in the PKCS#5 standard. The filling method of PCKS#5 is: the last data block of plaintext contains N bytes of filling data (N depends on the data length of the last block of plaintext). The following figure shows some examples of words with different lengths (FIG, BANANA, AVOCADO, PLANTAIN, passionfront) and their results filled with PKCs #5 (each data block is 8 bytes long).
--------
What is PKCS#5?
PKCS#5 is a filling standard designed by RSA information security company.
For the PKCS#5 standard, if several digits are missing, fill in the number.
For example, in the above example, we have three vacancies, so we should fill in all the vacancies. In this way, the content of the second group becomes' bc333 '.
One 0x01 (0x01)
Two 0x02 (0x02, 0x02)
Three 0x03 (0x03, 0x03, 0x03)
Four 0x04 (0x04, 0x04, 0x04, 0x04)
......
If the end of the last decrypted data block is not these legal byte sequences, most encryption / decryption programs will throw a fill exception. This exception is particularly critical for attackers. It is the basis of Padding Oracle Attack.