Binary Files
hacker@program-misuse~level51:~$ file /usr/bin/cat
/usr/bin/cat: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=b357ed53c8c9cb1a312f83b28982304effae0135, for GNU/Linux 3.2.0, stripped
ELF contains section headers
hacker@program-misuse~level51:~$ readelf -e /usr/bin/cat
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: DYN (Shared object file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x31f0
Start of program headers: 64 (bytes into file)
Start of section headers: 41496 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 13
Size of section headers: 64 (bytes)
Number of section headers: 30
Section header string table index: 29
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 0000000000000318 00000318
000000000000001c 0000000000000000 A 0 0 1
[ 2] .note.gnu.propert NOTE 0000000000000338 00000338
0000000000000020 0000000000000000 A 0 0 8
[ 3] .note.gnu.build-i NOTE 0000000000000358 00000358
0000000000000024 0000000000000000 A 0 0 4
[ 4] .note.ABI-tag NOTE 000000000000037c 0000037c
0000000000000020 0000000000000000 A 0 0 4
[ 5] .gnu.hash GNU_HASH 00000000000003a0 000003a0
000000000000006c 0000000000000000 A 6 0 8
[ 6] .dynsym DYNSYM 0000000000000410 00000410
0000000000000690 0000000000000018 A 7 1 8
[ 7] .dynstr STRTAB 0000000000000aa0 00000aa0
000000000000033d 0000000000000000 A 0 0 1
[ 8] .gnu.version VERSYM 0000000000000dde 00000dde
000000000000008c 0000000000000002 A 6 0 2
[ 9] .gnu.version_r VERNEED 0000000000000e70 00000e70
0000000000000060 0000000000000000 A 7 1 8
[10] .rela.dyn RELA 0000000000000ed0 00000ed0
0000000000000378 0000000000000018 A 6 0 8
[11] .rela.plt RELA 0000000000001248 00001248
0000000000000498 0000000000000018 AI 6 25 8
[12] .init PROGBITS 0000000000002000 00002000
000000000000001b 0000000000000000 AX 0 0 4
[13] .plt PROGBITS 0000000000002020 00002020
0000000000000320 0000000000000010 AX 0 0 16
[14] .plt.got PROGBITS 0000000000002340 00002340
0000000000000010 0000000000000010 AX 0 0 16
[15] .plt.sec PROGBITS 0000000000002350 00002350
0000000000000310 0000000000000010 AX 0 0 16
[16] .text PROGBITS 0000000000002660 00002660
0000000000003dc2 0000000000000000 AX 0 0 16
[17] .fini PROGBITS 0000000000006424 00006424
000000000000000d 0000000000000000 AX 0 0 4
[18] .rodata PROGBITS 0000000000007000 00007000
000000000000122c 0000000000000000 A 0 0 32
[19] .eh_frame_hdr PROGBITS 000000000000822c 0000822c
00000000000002bc 0000000000000000 A 0 0 4
[20] .eh_frame PROGBITS 00000000000084e8 000084e8
0000000000000ce8 0000000000000000 A 0 0 8
[21] .init_array INIT_ARRAY 000000000000aa90 00009a90
0000000000000008 0000000000000008 WA 0 0 8
[22] .fini_array FINI_ARRAY 000000000000aa98 00009a98
0000000000000008 0000000000000008 WA 0 0 8
[23] .data.rel.ro PROGBITS 000000000000aaa0 00009aa0
0000000000000198 0000000000000000 WA 0 0 32
[24] .dynamic DYNAMIC 000000000000ac38 00009c38
00000000000001f0 0000000000000010 WA 7 0 8
[25] .got PROGBITS 000000000000ae28 00009e28
00000000000001c8 0000000000000008 WA 0 0 8
[26] .data PROGBITS 000000000000b000 0000a000
00000000000000c0 0000000000000000 WA 0 0 32
[27] .bss NOBITS 000000000000b0c0 0000a0c0
0000000000000198 0000000000000000 WA 0 0 32
[28] .gnu_debuglink PROGBITS 0000000000000000 0000a0c0
0000000000000034 0000000000000000 0 0 4
[29] .shstrtab STRTAB 0000000000000000 0000a0f4
000000000000011d 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040
0x00000000000002d8 0x00000000000002d8 R 0x8
INTERP 0x0000000000000318 0x0000000000000318 0x0000000000000318
0x000000000000001c 0x000000000000001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x00000000000016e0 0x00000000000016e0 R 0x1000
LOAD 0x0000000000002000 0x0000000000002000 0x0000000000002000
0x0000000000004431 0x0000000000004431 R E 0x1000
LOAD 0x0000000000007000 0x0000000000007000 0x0000000000007000
0x00000000000021d0 0x00000000000021d0 R 0x1000
LOAD 0x0000000000009a90 0x000000000000aa90 0x000000000000aa90
0x0000000000000630 0x00000000000007c8 RW 0x1000
DYNAMIC 0x0000000000009c38 0x000000000000ac38 0x000000000000ac38
0x00000000000001f0 0x00000000000001f0 RW 0x8
NOTE 0x0000000000000338 0x0000000000000338 0x0000000000000338
0x0000000000000020 0x0000000000000020 R 0x8
NOTE 0x0000000000000358 0x0000000000000358 0x0000000000000358
0x0000000000000044 0x0000000000000044 R 0x4
GNU_PROPERTY 0x0000000000000338 0x0000000000000338 0x0000000000000338
0x0000000000000020 0x0000000000000020 R 0x8
GNU_EH_FRAME 0x000000000000822c 0x000000000000822c 0x000000000000822c
0x00000000000002bc 0x00000000000002bc R 0x4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 0x10
GNU_RELRO 0x0000000000009a90 0x000000000000aa90 0x000000000000aa90
0x0000000000000570 0x0000000000000570 R 0x1
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt
03 .init .plt .plt.got .plt.sec .text .fini
04 .rodata .eh_frame_hdr .eh_frame
05 .init_array .fini_array .data.rel.ro .dynamic .got .data .bss
06 .dynamic
07 .note.gnu.property
08 .note.gnu.build-id .note.ABI-tag
09 .note.gnu.property
10 .eh_frame_hdr
11
12 .init_array .fini_array .data.rel.ro .dynamic .got
Program Header Types
- INTERP: 定義了需要被load的library
- LOAD: 定義了需要被放入記憶體的ELF 檔案part
ELF Section Headers, 可能並不在ELF中。只有segments是必要的,section headers只是一些可選的metadata
.text: executable code of the program
.plt and .got: used to resolve and dispatch library calls
.data: pre-initilized global writable data
.rodata: global read-only data, such as string constants
.bss: uninitialized global writable data
常用工具:
- gcc
- readelf
- objdump: disassemble
- nm: view the symbols
- patchelf: change some properties
- objcopy: swap out ELF sections
- strip: remove some information, like symbols
- https://ide.kaitai.io/
- kaitai的解析在obj tree上
https://intezer.com/blog/research/executable-linkable-format-101-part1-sections-segments/
Overview
ELF檔案通常包含以下元素:ELF Header, Sections和Segments。只有Segment是必要的
ELF Headers
General information
readelf -h main
struct Elf64_Ehdr
- e_ident: Array of 16 bytes containing identification flags about the file, which serve to decode and interpret the file’s contents. Examples of these identification flags include:
- EI_MAG0-3: ELF magicELF Header
- EI_CLASS: File class.
- EI_DATA: File’s data encoding.
- EI_VERSION: File’s version.
- EI_OSABI: OS/ABI identification.
- EI_ABIVERSION: ABI version
- EI_PAD: Start of padding bytes.
- EI_NIDENT: Size of ei_ident.
- e_type: Type of executable.
- e_machine: File’s architecture.
- e_version: Object file version.
- e_entry: Entry point of application.
- e_phoff: File offset of the Program Header Table.
- e_shoff: File offset of the Section Header Table.
- e_flags: Processor-specific flags associated with the file.
- e_ehsize: ELF header size.
- e_phentsize: Program Header entry size in Program Header Table.
- e_phnum: Number of Program Headers.
- e_shentsize: Section Header entry size in Section Header Table.
- e_shnum: Number of Section Headers.
- e_shstrndx: index in Section Header Table Denoting Section dedicated to Hold Section names.
ELF Sections
用於連結的資訊
readelf -S main
struct ELF64_Shdr
- sh_name: index of section name in section header string table.ELF Header
- sh_type: section type.
- sh_flags: section attributes.
- sh_addr: virtual address of section.
- sh_offset: section offset in disk.
- sh_size: section size.
- sh_link: section link index.
- sh_Info: Section extra information.
- sh_addralign: section alignment.
- sh_entsize: size of entries contained in section.
common sections:
- .text: code.
- .data: initialised data.
- .rodata: initialised read-only data.
- .bss: uninitialized data.
- .plt: PLT (Procedure Linkage Table) (IAT equivalent).
- .got: GOT entries dedicated to dynamically linked global variables.
- .got.plt: GOT entries dedicated to dynamically linked functions.
- .symtab: global symbol table.
- .dynamic: Holds all needed information for dynamic linking.
- .dynsym: symbol tables dedicated to dynamically linked symbols.
- .strtab: string table of .symtab section.
- .dynstr: string table of .dynsym section.
- .interp: RTLD embedded string.
- .rel.dyn: global variable relocation table.
- .rel.plt: function relocation table.
ELF Segments
描述如何將ELF放入記憶體,在link時不需要,又名為Program Header
Program Header Table
struct ELF64_Phdr
- p_type: Segment type.ELF Header
- p_flags: Segment attributes.
- p_offset: File offset of segment.
- p_vaddr: Virtual address of segment.
- p_paddr: Physical address of segment.
- p_filesz: Size of segment on disk.
- p_memsz: Size of segment in memory.
- P_align: segment alignment in memory
常見segment type
PT_NULL: unassigned segment (usually first entry of Program Header Table).
PT_LOAD: Loadable segment.
PT_INTERP: Segment holding .interp section.
PT_TLS: Thread Local Storage segment (Common in statically linked binaries).
PT_DYNAMIC: Holding .dynamic section.
Sections and Segments
segment將sections按照型別等合在一起了
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt
03 .init .plt .plt.got .plt.sec .text .fini
04 .rodata .eh_frame_hdr .eh_frame
05 .init_array .fini_array .data.rel.ro .dynamic .got .data .bss
06 .dynamic
07 .note.gnu.property
08 .note.gnu.build-id .note.ABI-tag
09 .note.gnu.property
10 .eh_frame_hdr
11
12 .init_array .fini_array .data.rel.ro .dynamic .got
segment的p_align必須要是system page size的倍數,防止多個segments被放入同一個memory page中。因為不同的segments的執行屬性不同
https://intezer.com/blog/malware-analysis/executable-linkable-format-101-part-2-symbols/
符號:機器碼中的符號可以只用offset和address來定義,但是ELF檔案中通常還包含更詳盡的符號資訊,用於dynamic linking和debugger。
struct Elf64_Sym{
Elf64_Word st_name; unsigned char st_info, st_other; Elf64_Half st_shndx; Elf64_Addr st_value; Elf64_Xword st_size;}
The Elements of this structure include:
- st_name: index in string table of symbol name. If this field is not initialized, then the symbol doesn’t have a name
- st_info: contains symbol bind and type attributes. Binding attributes determine the linkage visibility and behavior when a given symbol is referenced by an external object.
- The most common symbol binds are the following:
- STB_LOCAL: Symbol is not visible outside the ELF object containing the symbol definition.
- STB_GLOBAL: Symbol is visible to all object files.
- STB_WEAK: Representing global symbols, but their definition can be overridden.
- The most common symbol types are the following:
- STT_NOTYPE: symbol type is not specified
- STT_OBJECT: symbol is a data object (variable).
- STT_FUNC: symbol is a code object (function).
- STT_SECTION: symbol is a section.
- In order to retrieve these fields, a set of bitmasks are used. These bitmasks include:
- ELF64_ST_BIND(info) ((info) >> 4)
- ELF64_ST_TYPE(info) ((info) & 0xf)
- The most common symbol binds are the following:
- st_other: information about symbol visibility. Symbol visibility defines how a given symbol may be accessed once the symbol has become part of an executable or shared object. Some common symbol visibilities are the following:
- STV_DEFAULT: for default visibility symbols, its attribute is specified by the symbol’s binding type.
- STV_PROTECTED: symbol is visible by other objects, but cannot be preempted.
- STV_HIDDEN: symbol is not visible to other objects.
- STV_INTERNAL: symbol visibility is reserved.
- ELF64_ST_VISIBILITY(o) ((o) & 0x3)
- st_shndx: Every symbol entry within the Symbol Table is associated with a section. This value specifies the section index within the Section Header Table associated with the given symbol. Common values for section type field include:
- SHT_UNDEF: section is not present in current object. This value is typically set in symbols that have been imported from external objects.
- SHT_PROGBITS: section is defined in current object.
- SHT_SYMTAB, SHT_DYNSYM: Symbol Table (.symtab, .dynsym).
- SHT_STRTAB, SHT_DYNSTR: String Table (.strtab, .dynstr).
- SHT_REL: Relocation Table without explicit addends (.rel.dyn, .rel.plt).
- SHT_RELA: Relocation Table with explicit addends (.rela.dyn, .rela.plt).
- SHT_HASH: Hash Table for dynamic symbol resolution (.gnu.hash)
- SHT_DYNAMIC: section holding Dynamic Linking information (.dynamic)
- SHT_NOBITS: section takes no space in disk (.bss).
- st_value: This field specifies the symbol value for a given Symbol Table entry. The interpretation of this field can vary depending on the object type.
- For ET_REL files st_value holds a section offset. The section in which this offset resides is specified on its st_shndx field.
- For ET_EXEC / ET_DYN files, st_value holds a virtual address. If this field contains a value of 0 and the symbol’s section pointed by st_shndx has a sh_type field of type SHT_UNDEF, the symbol is an imported relocation, and its value will be resolved at runtime by the RTLD (ld.so).
- st_size: Field containing symbol’s size.
st_info
高4位是symbol binding, st_other
低2位是symbol visibility。符號可見性由該符號的宿主物件繫結,應用於任何引用它的外部物件,讓static linking能夠看到符號,允許override。symbol binding則是由the object that ref the symbol決定,讓dynamic linking(dynamic loader)允許載入其定義,允許preempt. STB_GLOBAL
可以override STB_WEAK
。STB_GLOBAL
可被preempted
// libmath.c
int add(int a, int b) {
return a + b;
}
__attribute__((visibility("hidden")))
int subtract(int a, int b) {
return a - b;
}
STB_GLOBAL with STV_HIDDEN:
This combination is also possible. It means the symbol is globally visible (STB_GLOBAL) but not visible to other object files or shared libraries (STV_HIDDEN).
If a program tries to use this symbol, the linker will successfully resolve the symbol, but it will not be exported by the dynamic linker, making it inaccessible to other components linked against the same shared library or object file.
效果同STB_LOCAL
和STV_HIDDEN
差不多,但是可能可以被用於最佳化
符號表
https://intezer.com/blog/malware-analysis/executable-and-linkable-format-101-part-3-relocations/
https://intezer.com/blog/malware-analysis/executable-linkable-format-101-part-4-dynamic-linking/
Linux Process Loading
Linux Process Execution
Helpful Resources
For launching programs from Python, we recommend using pwntools, but subprocess should work as well. If you are not using one of these two, you will suffer heavily when you get to input redirection (for that, check out the stdin and stdout arguments to pwn.process or subprocess.Popen).
For reading and writing directly to file descriptors in bash, check out the read and echo builtins.
You will find the env command useful, and the exec bash builtin.
Quick refreshers on fork() versus exec*() here and here and here.
Remember to wait() on your children! If you use subprocess.Popen, pwn.process, or good old fork(), your parent will keep executing and, unless it waits for the child in some way, will just terminate! This is almost never what you want.
Some documentation on networking in C.
Useful resource for pipes in C.
Useful resource for FIFOs in C.
A treatise on I/O redirection in Linux shells, which has applications in this assignment: https://web.archive.org/web/20220629044814/http://bencane.com:80/2012/04/16/unix-shell-the-art-of-io-redirection/
A guide on Linux symbolic links. https://www.nixtutor.com/freebsd/understanding-symbolic-links/
A video tutorial on FIFOs in C.
A great visual guide to I/O redirection in Linux.
An incredible pwntools cheatsheet by a pwn.college student!
A deep dive into the history and technology behind command line terminals.
Practices
繼承父程序的File Descriptor可能帶來潛在危險: File descriptors are inherited from the parent, unless the FD_CLOEXEC is set by the parent on the file descriptor. For security reasons, some programs, such as python, do this by default in certain cases. Be careful if you are creating and trying to pass in FDs in python.
launch program with empty env: env -i /challenge/embryoio_level7
process.cmdline()
如果process parent終止了但是子程序沒有終止,parent process就會成為PID=1的那個程序 This process is the initialization process of your docker container (aka PID 1). When the parent of a process terminates, that process is 'reparented' to PID 1. So, the likely situation here is that your parent process terminated before waiting on the child. Go fix that 😃. Look into waitpid() in C, process.wait() for pwntools, or Popen.wait() for subprocess
The use of system() or popen() to execute the challenge. Both will actually execute a shell that will then execute the challenge, so the parent of the challenge will be that shell, rather than your program.
bash會新增環境變數,如env -i bash 1.sh
得到的env就不為空,必須要echo "env -i xxx" > 1.sh && bash 1.sh
才能獲得真正空的env
level73實際上還是要寫c程式,用chdir開啟目錄後以exec轉給checker.py
template1.c
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
int pwncollege(){return 0;}
int main(int argc, char * argv[], char * envp[]){
int status1;
int pid1;
int fd1[2];
/*if(pipe(fd1) == -1){
puts("pipe error");
}*/
pid1 = fork();
if(pid1 < 0){
puts("fork error");
}
else if(pid1 == 0)
{
chdir("/tmp/jdspva");
int fdin = open("taislg", 0);
dup2(fdin, STDIN_FILENO);
char cname[] ="/challenge/embryoio_level84";
envp[0] = strdup("341=jripfwmtnw");
envp[1] = NULL;
char * subargv[300];
for(int i = 0;i < 300;i++){subargv[i] = strdup("1");}
subargv[0] = strdup(cname);
subargv[234]=strdup("puzltwgeyb");
subargv[299]=NULL;
execvpe(cname, subargv, envp);
}else{
int rc_wait = waitpid(pid1, &status1, 0);
if(rc_wait < 0){
perror("waitpid");
exit(EXIT_FAILURE);
}
}
return -1;
}
template2.c
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
int pwncollege(){return 0;}
int main(int argc, char * argv[], char * envp[]){
int status1, status2;
int pid1, pid2;
int fd1[2];
char cname[] ="/challenge/embryoio_level66";
if(pipe(fd1) == -1){
puts("pipe error");
}
pid1 = fork();
if(pid1 < 0){
puts("fork error");
}
else if(pid1 == 0)
{
close(fd1[0]);
dup2(fd1[1], STDOUT_FILENO);
execl("/usr/bin/cat", "/usr/bin/cat", "1.txt", NULL);
}else{
pid2 = fork();
if(pid2 == 0){
close(fd1[1]);
dup2(fd1[0], STDIN_FILENO);
// usleep(1000000);
execl(cname, cname, NULL);
}else{
close(fd1[0]);
close(fd1[1]);
int rc_wait = waitpid(pid1, &status1, 0);
int rc2_wait = waitpid(pid2, &status2, 0);
if(rc_wait < 0 || rc2_wait < 0){
perror("waitpid");
exit(EXIT_FAILURE);
}
printf("exited, status=%d, %d\n", WEXITSTATUS(status1), WEXITSTATUS(status2));
}
}
return -1;
}
template.sh
echo $(find /challenge/ -name e* -print -quit) > 1.sh
template2.sh
echo "arr=();for i in {0..300};do arr+=(\$i);done;arr[206]=\"fmdjvkkfwe\";arr[96]=\"vjgptigtxt\";echo \"\${arr[@]}\";env -i 207=fmdjvkkfwe $(find /challenge/ -name "e*" -print -quit) \"\${arr[@]}\"" > 1.sh
template1.py
import subprocess
import pathlib
cname=list(pathlib.Path("/challenge").glob("e*"))[0]
print(cname)
prog=subprocess.Popen([cname], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
while prog.poll() is None:
print("readline")
line = prog.stdout.readline().decode('utf8')
print(line)
print("over")
template3.c
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
int pwncollege(){return 0;}
int main(int argc, char * argv[], char * envp[]){
mkfifo("pipein", 640);
mkfifo("pipeout", 640);
int pid = fork();
if (pid < 0){
perror("fork error");
}else if(pid == 0){
int fdpipein = open("pipein", O_RDONLY);
int fdpipeout = open("pipeout", O_WRONLY);
sleep(5);
dup2(fdpipein, 0);
dup2(fdpipeout, 1);
printf("try to read, %d\n", fdpipein);
char cname[] ="/challenge/embryoio_level117";
execl(cname, cname, NULL);
}else{
int fdpipein = open("pipein", O_WRONLY);
int fdpipeout = open("pipeout", O_RDONLY);
printf("try to write from parent\n");
write(fdpipein, "qhrysiws\n", strlen("qhrysiws\n"));
printf("try to reads from parent\n");
char buff[10240];
while(1){
int slen = read(fdpipeout, buff, 10240);
buff[slen] = 0;
puts(buff);
sleep(3);
}
wait(NULL);
}
return 0;
}
level87 用coproc能很好地解決程式提前退出導致的brokenpipe問題
level88:
argv[0] is passed into the execve() system call separately from the program path to execute.This means that it does not have to be the same as the program path, and that you can actually control it. This is done differently for different methods of execution. For example, in C, you simply need to pass in a different argv[0]. Bash has several ways to do it, but one way is to use a combination of a symbolic link (e.g., the ln -s
command) and the PATH environment variable.
level92:
如果"echo someinput>pipein"不設定為後臺程序就會卡住,因為pipe就是需要同時讀寫
level93:
任何程式如果開啟了named pipe並且退出了,就會發一個EOF訊號導致另一端的程式崩潰,解決方案是指令碼中把這個named pipe佔用了,用exec open。exec 11>pipein; echo "$result" >& 11 &
level95:
- 注意redirect方向,當為了prog開啟pipein而提前開啟pipein並dup為file descriptor2的時候,bash和prog都在讀
- 直接將輸出的file descriptor對映為輸入的file descriptor0,
0&<2
似乎不行。如下程式碼也不行,會使得prog無法讀到對應的file descriptor2,進而開始讀取stdincname=$(find /challenge -name e* -print -quit) $cname <&2 & sleep 1 echo "jeyrtdxd" >&2 # only print to the bash console, rather than being feed into $cname # type jeyrtdxd and get the flag
- 類似
(echo xxx) |& prog
,(echo "nrnxowap" &>2) 2> >($cname)
之類的方法都會使得prog無法讀到對應的file descriptor2,進而開始讀取stdin - 注意如果沒加回車 python的input會報EOFError
- 順序:1. 在啟動checker.py之前就已經向輸入的管道內寫 2. 用exec開啟對應管道,防止EOF
level98:
- 對單引號,方括號等放不進命令列regex的字元,可以用十六進位制
\x27
escape。 - 區分awk和gawk
- 注意賦值命令返回多行結果給變數的時候預設為一整個變數,而不是一個陣列。用readarray才能把多行轉化為一個陣列
- 用
((i++))
而不是i++
- 注意sed等的引數需要是單引號而不是雙引號,因為雙引號允許variable expansion等
level99:
寫入stdin後如果不重新整理就卡死了
Unlike some other popen functions, this implementation will never implicitly call a system shell. This means that all characters, including shell metacharacters, can safely be passed to child processes. If the shell is invoked explicitly, via shell=True, it is the application’s responsibility to ensure that all whitespace and metacharacters are quoted appropriately to avoid shell injection vulnerabilities. On some platforms, it is possible to use shlex.quote() for this escaping.
From https://docs.python.org/3/library/subprocess.html#subprocess.Popen.communicate
level107: open() creates new file objects, os.open() creates OS-level file descriptors, and os.fdopen() creates a file object out of a file descriptor
如果不寫換行符,則python會從鍵盤讀取
注意close_fds
引數!!!!這個引數預設為True,會關掉所有父程序的檔案描述符!
level122:
和之前的用fd1來輸入一樣,即使沒有做任何dup,直接從鍵盤輸入就能得到key。(這個section大部分都可以這樣做,用失敗的dup2讓os.path.realpath(f"/proc/{pid}/fd/{fd}")
中確實有path,然後就從shell直接手動輸入答案)
如果想要避免這種失敗的dup,嚴格按照題目描述透過fd1來輸入,則需要巧妙控制父程序,讓子程序輸入輸出父程序輸入共用同一個pipe,而父程序快速讀完這個pipe中子程序的提示性輸出,儘可能不要做任何其他事情。利用時間差防止父子程序自己讀自己。首先父程序快速讀完prompt,然後父程序輸入pasword,父程序這之後要相當一段時間不做讀取,讓子程序讀取並完成最後的全部流程,最後父程序再去讀flag。
level124:
- 關鍵函式raise(3) Sends a signal to the calling thread. kill(2) Sends a signal to a specified process, to all members of a specified process group, or to all processes on the system.
- 試著把訊號的字串名稱轉化為數字,但是strsignal只給了描述。最後解析了
kill -l
輸出的訊號表進行查詢 - sigdescr_np() and sigabbrev_np() first appeared in glibc 2.32. 而實驗環境只有glibc 2.31,只能用
sys_siglist
但是也是描述而不是訊號名。//enable sigabbrev_np #define _GNU_SOURCE #include <string.h>
level125
- 多加了一種運算
^
,按位異或,但是在bc中是按照冪次理解的,所以不能用bc。python裡面是定義這個運算子為按位異或的
level129:
- 一個bash只能有一個coproc, There may be only one active coprocess at a time.
- 用
coproc name {cat | prog|cat}
報錯,但是coproc name (cat | prog|cat)
就可以
level139:
- 忘掉用的是execl,使用了"cat"而不是"/usr/bin/cat",導致建立cat程序失敗,在ps aux中顯示為
./1.out <defunct>
,但是問題是導致了後續的check.py的父程序為1,對應/sbin/docker-init -- /bin/sleep 6h
,還以為是其他的什麼問題
level140:
- 一開始使用"socat -u - tcp-listen:
"和"socat -u PIPE:pipein tcp-listen: ",後來才意識到checker.py自己會listen,無需建立一個程序用來listen這個埠。而且因為只接受單個連線,甚至不能開兩個讀寫。 def listen_dup(port): print(f"[INFO] This challenge is a network server, and will only communicate on TCP port {port}.") s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind(('localhost', port)) s.listen() c,_ = s.accept() print(f"[INFO] Connection received! All further communication will happen through the TCP connection.") os.dup2(c.fileno(), 0) os.dup2(c.fileno(), 1) os.dup2(c.fileno(), 2)
- 失敗了之後重啟指令碼時,前後兩次指令碼啟動過於頻繁有時會導致報錯該地址早已使用。用"netstat -ltnp"看不到什麼程序在listen這個埠。等幾秒就行。
- checker.py的通訊分兩個階段,在listen之前,輸出提示的階段,是完全按照普通的stdin, stdout來互動的。在listen之後,標準輸入和輸出都被重定向這個連結了,而且不接受其他連線,因此需要兩階段的處理方式不同。
- 一開始為了嚴謹在標準輸入輸出被dup之後關掉了stdout對應的pipeout,但是關的太快了,還沒有等到輸出dup之前的那句" Connection received! All further communication will happen through the TCP connection.",就一直報錯BrokenPipe,還以為是不能同時讀寫。
level142
- On Unix-like systems, including Linux, /dev/tcp and /dev/udp are special files used by Bash and other shells for network socket operations, but they are not part of the standard file system and cannot be directly accessed using open() in C.