Bug Hunting with Structural Code Search Rijnard van Tonder @rvtond - - PowerPoint PPT Presentation

bug hunting with structural code search
SMART_READER_LITE
LIVE PREVIEW

Bug Hunting with Structural Code Search Rijnard van Tonder @rvtond - - PowerPoint PPT Presentation

Bug Hunting with Structural Code Search Rijnard van Tonder @rvtond grep Regular expression search for plain text Good for bug hunting 2 grep example on libssh2 ALLOC\([^,]*,[^;]*[*][^;]*\); 3 [1]


slide-1
SLIDE 1

Rijnard van Tonder

Bug Hunting with Structural Code Search

@rvtond

slide-2
SLIDE 2
  • Regular expression search for plain text
  • Good for bug hunting

2

grep

slide-3
SLIDE 3

3

grep example on libssh2

ALLOC\([^,]*,[^;]*[*][^;]*\);

[1] https://papers.put.as/papers/macosx/2013/SyScan2013_Stefan_Esser_Mountain_Lion_iOS_Vulnerabilities_Garage_Sale.pdf

slide-4
SLIDE 4

4

grep example on libssh2

ALLOC\([^,]*,[^;]*[*][^;]*\);

slide-5
SLIDE 5

5

grep example on libssh2

ALLOC\([^,]*,[^;]*[*][^;]*\);

session->userauth_kybd_num_prompts = _libssh2_ntohu32(s); s += 4; if(session->userauth_kybd_num_prompts) { session->userauth_kybd_prompts = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_USERAUTH_KBDINT_PROMPT) * session->userauth_kybd_num_prompts); if (!session->userauth_kybd_prompts) { _libssh2_error(session, LIBSSH2_ERROR_ALLOC, LIBSSH2_ALLOC(session, sizeof(LIBSSH2_USERAUTH_KBDINT_PROMPT) * session->userauth_kybd_num_prompts); }

The bug: attacker controlled alloc size => integer overflow

slide-6
SLIDE 6

6

grep example on libssh2

ALLOC\([^,]*,[^;]*[*][^;]*\);

session->userauth_kybd_num_prompts = _libssh2_ntohu32(s); s += 4; if(session->userauth_kybd_num_prompts) { session->userauth_kybd_prompts = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_USERAUTH_KBDINT_PROMPT) * session->userauth_kybd_num_prompts); if (!session->userauth_kybd_prompts) { _libssh2_error(session, LIBSSH2_ERROR_ALLOC, LIBSSH2_ALLOC(session, sizeof(LIBSSH2_USERAUTH_KBDINT_PROMPT) * session->userauth_kybd_num_prompts); }

slide-7
SLIDE 7

7

grep example on libssh2

session->userauth_kybd_num_prompts = _libssh2_ntohu32(s); s += 4; if(session->userauth_kybd_num_prompts) { session->userauth_kybd_prompts = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_USERAUTH_KBDINT_PROMPT) * session->userauth_kybd_num_prompts); if (!session->userauth_kybd_prompts) { _libssh2_error(session, LIBSSH2_ERROR_ALLOC, LIBSSH2_ALLOC(session, sizeof(LIBSSH2_USERAUTH_KBDINT_PROMPT) * session->userauth_kybd_num_prompts); }

ALLOC\([^,]*,[^;]*[*][^;]*\);

slide-8
SLIDE 8

8

grep example on libssh2

ALLOC\([^,]*,[^;]*[*][^;]*\);

METASYNTAX (REPEAT)

slide-9
SLIDE 9

9

grep example on libssh2

ALLOC\([^,]*,[^;]*[*][^;]*\);

TEXT (MULTIPLY OP)

slide-10
SLIDE 10

session->userauth_kybd_num_prompts = _libssh2_ntohu32(s); s += 4; if(session->userauth_kybd_num_prompts) { session->userauth_kybd_prompts = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_USERAUTH_KBDINT_PROMPT) * session->userauth_kybd_num_prompts); if (!session->userauth_kybd_prompts) { _libssh2_error(session, LIBSSH2_ERROR_ALLOC, LIBSSH2_ALLOC(session, sizeof(LIBSSH2_USERAUTH_KBDINT_PROMPT) * session->userauth_kybd_num_prompts);

10

grep example on libssh2

ALLOC\([^,]*,[^;]*[*][^;]*\);

TEXT (MULTIPLY OP) TEXT (MULTIPLY OP)

slide-11
SLIDE 11

session->userauth_kybd_num_prompts = _libssh2_ntohu32(s); s += 4; if(session->userauth_kybd_num_prompts) { session->userauth_kybd_prompts = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_USERAUTH_KBDINT_PROMPT) * session->userauth_kybd_num_prompts); if (!session->userauth_kybd_prompts) { _libssh2_error(session, LIBSSH2_ERROR_ALLOC, LIBSSH2_ALLOC(session, sizeof(LIBSSH2_USERAUTH_KBDINT_PROMPT) * session->userauth_kybd_num_prompts);

11

grep example on libssh2

ALLOC\([^,]*,[^;]*[*][^;]*\);

slide-12
SLIDE 12

12

grep example on libssh2

  • Code structure matters

ALLOC\([^,]*,[^;]*[*][^;]*\);

slide-13
SLIDE 13

13

grep example on libssh2

  • Code structure matters

ALLOC\([^,]*,[^;]*[*][^;]*\);

slide-14
SLIDE 14

14

grep example on libssh2

  • Code structure matters
  • Can we do better?

ALLOC\([^,]*,[^;]*[*][^;]*\);

slide-15
SLIDE 15

15

grep example on libssh2

ALLOC\([^,]*,[^;]*[*][^;]*\);

slide-16
SLIDE 16

16

grep example on libssh2

comby

ALLOC\([^,]*,[^;]*[*][^;]*\);

slide-17
SLIDE 17

17

grep example on libssh2

comby

ALLOC\([^,]*,[^;]*[*][^;]*\); ALLOC(:[1],:[2]*:[3]);

slide-18
SLIDE 18

18

grep example on libssh2

comby

ALLOC\([^,]*,[^;]*[*][^;]*\); ALLOC(:[1],:[2]*:[3]);

HOLES BIND IDENTIFIERS TO SYNTAX

slide-19
SLIDE 19

19

grep example on libssh2

comby

ALLOC\([^,]*,[^;]*[*][^;]*\); ALLOC(:[1],:[2]*:[3]);

CONCRETE SYNTAX

slide-20
SLIDE 20

20

grep example on libssh2

comby

ALLOC\([^,]*,[^;]*[*][^;]*\); ALLOC(:[1],:[2]*:[3]);

BALANCED DELIMITERS

slide-21
SLIDE 21

21

grep example on libssh2

comby

ALLOC\([^,]*,[^;]*[*][^;]*\); ALLOC(:[1],:[2]*:[3]);

BALANCED DELIMITERS NESTED CODE STRUCTURES. LANGUAGE-AWARE.

slide-22
SLIDE 22

22

Assembly, Bash, C/C++, C#, Clojure, CSS, Dart, Elm, Elixir, Erlang, Fortran, F#, Go, Haskell, HTML/XML, Java, Javascript, JSX, JSON, Julia, LaTeX, Lisp, Nim, OCaml, Pascal, PHP, Python, Reason, Ruby, Rust, Scala, SQL, Swift, Plain Text, TSX, Typescript

comby supports ~all the languages

slide-23
SLIDE 23

23

Assembly, Bash, C/C++, C#, Clojure, CSS, Dart, Elm, Elixir, Erlang, Fortran, F#, Go, Haskell, HTML/XML, Java, Javascript, JSX, JSON, Julia, LaTeX, Lisp, Nim, OCaml, Pascal, PHP, Python, Reason, Ruby, Rust, Scala, SQL, Swift, Plain Text, TSX, Typescript

comby supports ~all the languages

slide-24
SLIDE 24

24

comby on the command line

https://drive.google.com/open?id=1Ba-sOhmhRKCrUbdJVvh7mCyoj1aHMMZz

Find video file at the link

slide-25
SLIDE 25

25

[1] https://en.wikipedia.org/wiki/Tux_(mascot)#/media/File:Tux.png

comby on the Linux Kernel

slide-26
SLIDE 26

26

[1] https://en.wikipedia.org/wiki/Tux_(mascot)#/media/File:Tux.png

comby on the Linux Kernel

[2] https://securitylab.github.com/disclosures

slide-27
SLIDE 27

27

[1] https://en.wikipedia.org/wiki/Tux_(mascot)#/media/File:Tux.png

comby on the Linux Kernel

[3] https://lkml.org/lkml/2019/9/9/487 [2] https://securitylab.github.com/disclosures

slide-28
SLIDE 28

28

[1] https://en.wikipedia.org/wiki/Tux_(mascot)#/media/File:Tux.png

comby on the Linux Kernel

[3] https://lkml.org/lkml/2019/9/9/487 [2] https://securitylab.github.com/disclosures

alloc workqueue is _ not getting checked

slide-29
SLIDE 29

29

[1] https://en.wikipedia.org/wiki/Tux_(mascot)#/media/File:Tux.png

comby on the Linux Kernel

[3] https://lkml.org/lkml/2019/9/9/487 [2] https://securitylab.github.com/disclosures

alloc workqueue is _ not getting checked

alloc_workqueue(:[args]);

slide-30
SLIDE 30

30

comby on the Linux Kernel

alloc_workqueue(:[args]);

WON’T MATCH COMMENTS

slide-31
SLIDE 31

31

comby on the Linux Kernel

alloc_workqueue(:[args]);

274 CALLS

slide-32
SLIDE 32

32

comby on the Linux Kernel

alloc_workqueue(:[args]);

USUALLY FOLLOWED BY AN ‘IF’ CHECK

ppd->hfi1_wq = alloc_workqueue( "hfi%d_%d", WQ_SYSFS | WQ_HIGHPRI | WQ_CPU_INTENSIVE | WQ_MEM_RECLAIM, HFI1_MAX_ACTIVE_WORKQUEUE_ENTRIES, dd->unit, pidx); if (!ppd->hfi1_wq) goto wq_error;

slide-33
SLIDE 33

33

comby on the Linux Kernel

alloc_workqueue(:[args]); :[[word]]

RULES PLACE CONSTRAINTS ON MATCHES

where :[[word]] != “if”

slide-34
SLIDE 34

34

comby on the Linux Kernel

alloc_workqueue(:[args]); :[[word]]

SOMETIMES A DIFFERENT FLAVOR

where :[[word]] != “if”

cgroup_destroy_wq = alloc_workqueue("cgroup_destroy", 0, 1); BUG_ON(!cgroup_destroy_wq);

slide-35
SLIDE 35

35

comby on the Linux Kernel

alloc_workqueue(:[args]); :[[word]] where :[[word]] != “if”, :[[word]] != “BUG_ON”

slide-36
SLIDE 36

36

comby on the Linux Kernel

alloc_workqueue(:[args]); :[[word]] where :[[word]] != “if”, :[[word]] != “BUG_ON”

  • 38 calls left
slide-37
SLIDE 37

37

comby on the Linux Kernel

alloc_workqueue(:[args]); :[[word]] where :[[word]] != “if”, :[[word]] != “BUG_ON”

  • 38 calls left
  • 1.5 minutes
slide-38
SLIDE 38

38

comby on the Linux Kernel

alloc_workqueue(:[args]); :[[word]] where :[[word]] != “if”, :[[word]] != “BUG_ON”

  • 38 calls left
  • 1.5 minutes

45 /* The lpfc_wq workqueue for deferred irq use */ 46 phba->wq = alloc_workqueue("lpfc_wq", WQ_MEM_RECLAIM, 0);

drivers/scsi/lpfc/lpfc_init.c

slide-39
SLIDE 39

39

comby on the Linux Kernel

alloc_workqueue(:[args]); :[[word]] where :[[word]] != “if”, :[[word]] != “BUG_ON”

  • 38 calls left
  • 1.5 minutes

4500 adapter->priv_checkbt_wq = alloc_workqueue("sdio_wq", 0, 0); 4501 INIT_DELAYED_WORK(&adapter->checkbt_work, (void *)check_bt_status_work);

drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c

slide-40
SLIDE 40

40

comby on the Linux Kernel

[1] https://lore.kernel.org/lkml/20190911201100.11483-1-navid.emamdoost@gmail.com/

logger->log_workqueue = create_singlethread_workqueue("cros_usbpd_log"); + if (!logger->log_workqueue) + return -ENOMEM;

slide-41
SLIDE 41

41

comby on the Linux Kernel

alloc_workqueue(:[args]); :[[word]]

[1] https://lore.kernel.org/lkml/20190911201100.11483-1-navid.emamdoost@gmail.com/

create_singlethread_workqueue(:[args]);

logger->log_workqueue = create_singlethread_workqueue("cros_usbpd_log"); + if (!logger->log_workqueue) + return -ENOMEM;

slide-42
SLIDE 42

comby on cpython

42

... 996 if (!wlen) 997 return PyErr_SetFromWindowsErr(0); 998 999 wbuf = (wchar_t*)PyMem_Malloc(wlen * sizeof(wchar_t)); 1000 1001 Py_BEGIN_ALLOW_THREADS 1002 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len, wbuf, wlen); 1003 if (wlen) { 1004 res = WriteConsoleW(self->handle, wbuf, wlen, &n, NULL); ...

Modules/_io/winconsoleio.c

[1] https://commons.wikimedia.org/wiki/File:Python-logo-notext.svg

slide-43
SLIDE 43

comby on kubernetes

43

[1] https://en.wikipedia.org/wiki/Kubernetes#/media/File:Kubernetes_logo_without_workmark.svg

slide-44
SLIDE 44

comby on kubernetes

44

slide-45
SLIDE 45

comby on kubernetes

45

func (sh *suffixHandler) interpret(...) (...) { // ... parsed, err := strconv.ParseInt(string(suffix[1:]), 10, 64) if err != nil { return 0, 0, DecimalExponent, false } return 10, int32(parsed), DecimalExponent, true }

slide-46
SLIDE 46

comby on kubernetes

46

func (sh *suffixHandler) interpret(...) (...) { // ... parsed, err := strconv.ParseInt(string(suffix[1:]), 10, 64) if err != nil { return 0, 0, DecimalExponent, false } return 10, int32(parsed), DecimalExponent, true }

PARSED AS INT64 PARSED AS INT64

slide-47
SLIDE 47

comby on kubernetes

47

func (sh *suffixHandler) interpret(...) (...) { // ... parsed, err := strconv.ParseInt(string(suffix[1:]), 10, 64) if err != nil { return 0, 0, DecimalExponent, false } return 10, int32(parsed), DecimalExponent, true }

CONVERTED TO INT32 => COULD OVERFLOW

slide-48
SLIDE 48

comby on kubernetes

48

kubectl expose deployment nginx-deployment --port 4294967377 --target-port 80 service/nginx-deployment exposed

slide-49
SLIDE 49

comby on kubernetes

49

kubectl expose deployment nginx-deployment --port 4294967377 --target-port 80 service/nginx-deployment exposed

slide-50
SLIDE 50

comby on kubernetes

50

func (sh *suffixHandler) interpret(...) (...) { // ... parsed, err := strconv.ParseInt(string(suffix[1:]), 10, 64) if err != nil { return 0, 0, DecimalExponent, false } return 10, int32(parsed), DecimalExponent, true }

slide-51
SLIDE 51

comby on kubernetes

51

func (sh *suffixHandler) interpret(...) (...) { // ... parsed, err := strconv.ParseInt(string(suffix[1:]), 10, 64) if err != nil { return 0, 0, DecimalExponent, false } return 10, int32(parsed), DecimalExponent, true }

:[[v]], err := strconv.ParseInt(:[1], :[2], 64)

slide-52
SLIDE 52

comby on kubernetes

52

func (sh *suffixHandler) interpret(...) (...) { // ... parsed, err := strconv.ParseInt(string(suffix[1:]), 10, 64) if err != nil { return 0, 0, DecimalExponent, false } return 10, int32(parsed), DecimalExponent, true }

:[[v]], err := strconv.ParseInt(:[1], :[2], 64) :[rest]

slide-53
SLIDE 53

comby on kubernetes

53

func (sh *suffixHandler) interpret(...) (...) { // ... parsed, err := strconv.ParseInt(string(suffix[1:]), 10, 64) if err != nil { return 0, 0, DecimalExponent, false } return 10, int32(parsed), DecimalExponent, true }

:[[v]], err := strconv.ParseInt(:[1], :[2], 64) :[rest]

:[rest] stops matching inside {...}

slide-54
SLIDE 54

comby on kubernetes

54

where match :[rest] { | “int32(:[arg])” -> :[arg] == :[[v]] }

NESTED MATCH RULE ON INT32(…) FUNCTION

:[[v]], err := strconv.ParseInt(:[1], :[2], 64) :[rest]

slide-55
SLIDE 55

comby on kubernetes

55

where match :[rest] { | “int32(:[arg])” -> :[arg] == :[[v]] }

func (sh *suffixHandler) interpret(...) (...) { // ... parsed, err := strconv.ParseInt(string(suffix[1:]), 10, 64) if err != nil { return 0, 0, DecimalExponent, false } return 10, int32(parsed), DecimalExponent, true }

:[[v]], err := strconv.ParseInt(:[1], :[2], 64) :[rest]

slide-56
SLIDE 56

comby on kubernetes

56

where match :[rest] { | “int32(:[arg])” -> :[arg] == :[[v]] }

ARGUMENT MUST EQUAL PREVIOUS VARIABLE

:[[v]], err := strconv.ParseInt(:[1], :[2], 64) :[rest]

slide-57
SLIDE 57

comby on kubernetes

57

func (sh *suffixHandler) interpret(...) (...) { // ... parsed, err := strconv.ParseInt(string(suffix[1:]), 10, 64) if err != nil { return 0, 0, DecimalExponent, false } return 10, int32(parsed), DecimalExponent, true }

where match :[rest] { | “int32(:[arg])” -> :[arg] == :[[v]] }

Equal

:[[v]], err := strconv.ParseInt(:[1], :[2], 64) :[rest]

slide-58
SLIDE 58

comby on kubernetes

58

func (sh *suffixHandler) interpret(...) (...) { // ... parsed, err := strconv.ParseInt(string(suffix[1:]), 10, 64) if err != nil { return 0, 0, DecimalExponent, false } return 10, int32(parsed), DecimalExponent, true }

where match :[rest] { | “int32(:[arg])” -> :[arg] == :[[v]] } :[[v]], err := strconv.ParseInt(:[1], :[2], 64) :[rest]

slide-59
SLIDE 59

comby on kubernetes

59

where match :[rest] { | “int32(:[arg])” -> :[arg] == :[[v]] } :[[v]], err := strconv.ParseInt(:[1], :[2], 64) :[rest]

slide-60
SLIDE 60

comby on kubernetes

60

where match :[rest] { | “int32(:[arg])” -> :[arg] == :[[v]] | “int16(:[arg])” -> :[arg] == :[[v]] }

ADDITIONAL “OR” CASES

:[[v]], err := strconv.ParseInt(:[1], :[2], 64) :[rest]

slide-61
SLIDE 61

61

comby on the command line

https://drive.google.com/open?id=184f_0nxCRyHFi9LFQCeqJin-oA0So5gp

Find video file at the link

slide-62
SLIDE 62

62

Structural code search: an easier way to search syntax trees

COMBY

  • Effort
  • Generality
  • Complexity
  • Precision
  • Speed
slide-63
SLIDE 63

63

COMBY

  • Effort
  • Generality
  • Complexity
  • Precision
  • Speed

Structural code search: an easier way to search syntax trees

slide-64
SLIDE 64

64

COMBY

Assembly, Bash, C/C++, C#, Clojure, CSS, Dart, Elm, Elixir, Erlang, Fortran, F#, Go, Haskell, HTML/XML, Java, Javascript, JSX, JSON, Julia, LaTeX, Lisp, Nim, OCaml, Pascal, PHP, Python, Reason, Ruby, Rust, Scala, SQL, Swift, Plain Text, TSX, Typescript

  • Effort
  • Generality
  • Complexity
  • Precision
  • Speed

Structural code search: an easier way to search syntax trees

slide-65
SLIDE 65

Assembly, Bash, C/C++, C#, Clojure, CSS, Dart, Elm, Elixir, Erlang, Fortran, F#, Go, Haskell, HTML/XML, Java, Javascript, JSX, JSON, Julia, LaTeX, Lisp, Nim, OCaml, Pascal, PHP, Python, Reason, Ruby, Rust, Scala, SQL, Swift, Plain Text, TSX, Typescript

65

COMBY

  • Effort
  • Generality
  • Complexity
  • Precision
  • Speed

Structural code search: an easier way to search syntax trees

slide-66
SLIDE 66

66

https://comby.dev

https://gitter.im/comby-tools/community https://github.com/comby-tools/comby

@rvtond

ALLOC(:[1],:[2]*:[3]);

comby syntax