pwn.college Fundementals Program interaction

雪溯發表於2024-04-04

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

  1. INTERP: 定義了需要被load的library
  2. 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

常用工具:

  1. gcc
  2. readelf
  3. objdump: disassemble
  4. nm: view the symbols
  5. patchelf: change some properties
  6. objcopy: swap out ELF sections
  7. strip: remove some information, like symbols
  8. 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)
  • 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_WEAKSTB_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_LOCALSTV_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:

  1. 注意redirect方向,當為了prog開啟pipein而提前開啟pipein並dup為file descriptor2的時候,bash和prog都在讀
  2. 直接將輸出的file descriptor對映為輸入的file descriptor0, 0&<2似乎不行。如下程式碼也不行,會使得prog無法讀到對應的file descriptor2,進而開始讀取stdin
    cname=$(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
    
    
  3. 類似(echo xxx) |& prog , (echo "nrnxowap" &>2) 2> >($cname)之類的方法都會使得prog無法讀到對應的file descriptor2,進而開始讀取stdin
  4. 注意如果沒加回車 python的input會報EOFError
  5. 順序:1. 在啟動checker.py之前就已經向輸入的管道內寫 2. 用exec開啟對應管道,防止EOF

level98:

  1. 對單引號,方括號等放不進命令列regex的字元,可以用十六進位制\x27escape。
  2. 區分awk和gawk
  3. 注意賦值命令返回多行結果給變數的時候預設為一整個變數,而不是一個陣列。用readarray才能把多行轉化為一個陣列
  4. ((i++))而不是i++
  5. 注意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:

  1. 關鍵函式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.
  2. 試著把訊號的字串名稱轉化為數字,但是strsignal只給了描述。最後解析了kill -l輸出的訊號表進行查詢
  3. 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

  1. 多加了一種運算^,按位異或,但是在bc中是按照冪次理解的,所以不能用bc。python裡面是定義這個運算子為按位異或的

level129:

  1. 一個bash只能有一個coproc, There may be only one active coprocess at a time.
  2. coproc name {cat | prog|cat}報錯,但是coproc name (cat | prog|cat)就可以

level139:

  1. 忘掉用的是execl,使用了"cat"而不是"/usr/bin/cat",導致建立cat程序失敗,在ps aux中顯示為./1.out <defunct>,但是問題是導致了後續的check.py的父程序為1,對應/sbin/docker-init -- /bin/sleep 6h,還以為是其他的什麼問題

level140:

  1. 一開始使用"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)
    
    
  2. 失敗了之後重啟指令碼時,前後兩次指令碼啟動過於頻繁有時會導致報錯該地址早已使用。用"netstat -ltnp"看不到什麼程序在listen這個埠。等幾秒就行。
  3. checker.py的通訊分兩個階段,在listen之前,輸出提示的階段,是完全按照普通的stdin, stdout來互動的。在listen之後,標準輸入和輸出都被重定向這個連結了,而且不接受其他連線,因此需要兩階段的處理方式不同。
  4. 一開始為了嚴謹在標準輸入輸出被dup之後關掉了stdout對應的pipeout,但是關的太快了,還沒有等到輸出dup之前的那句" Connection received! All further communication will happen through the TCP connection.",就一直報錯BrokenPipe,還以為是不能同時讀寫。

level142

  1. 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.

相關文章