Legacy “business-oriented” languages present unique challenges for software security. Unfortunately, there is a lack of awareness about the security risks of these languages. In this post, we will focus on the security vulnerabilities inherent in business-oriented programming languages such as COBOL and RPG. We’ll also identify how Kiuwan Code Security helps uncover potential security flaws in existing code.Cobol and RPG: Security in business-oriented languages
Most development teams are aware of security issues in front-end layers (usually developed in Java and similar “modern” languages). There is a wealth of information on the subject. Teams frequently follow “secure coding practices” and use early detection facilities (like reviews, security-focused testing, code audits, or static and dynamic analysis tools) to improve their understanding of software security and detect/prevent security flaws. But teams that are developing or maintaining business applications, typically located at the “back-end” of layered architectures, may not be as aware of potential security issues.
When experts take a deeper look at the business layer, coded in COBOL, RPG, ABAP, and similar “business-oriented” languages, they find overwhelming evidence that security vulnerabilities plague legacy software.
There are multiple reasons for this situation. First, many companies did not have an official policy for “secure coding” at the time the backend layer was originally developed. Developers currently maintaining such applications may not understand how a security flaw is injected in a particular technology. They may not be aware of the security concerns for common programming constructs, or how to ensure that no security flaws are deployed to production. In addition, specific security-oriented test plans for applications under maintenance are uncommon. Finally, software security specialists typically focus more on web and mobile applications inframe applications are less familiar territory for them.
Security exposures and weaknesses have been present within legacy systems from the beginning. But now, applications developed in the 1980s are accessible via the Internet (even if only indirectly). As a result, the risk posed by a vulnerability is now much higher.
Security vulnerabilities, such as SQL injection, are often taken into consideration by organizations and development teams. But that is not always the case with the risks posed by information flow vulnerabilities, which can lead to sensitive data leaking from the intended security domain and reaching unauthorized users.
Characterizing sensitive information is a difficult problem in static analysis. So is defining the boundaries between security domains, or determining when data access/modification escapes the proper domain. But these are critical issues. The risks of information flow vulnerabilities are at least as important for business-oriented languages as potential technical flaws. For example, the effect of exposing customer data such as government ID numbers or credit card numbers would be far more devastating than a crash caused by a buffer overflow attack.
Let’s start our discussion with the venerable COBOL, born in 1959. Why does COBOL remain in the software industry? In many cases, the answer is legacy lock-in. An organization that developed a large, critical application in COBOL may not have the resources to rewrite it from scratch using a modern language.
Some new backend systems are developed in COBOL. But pure corrective maintenance is rare since the code “works.” Most effort in COBOL apps today is devoted to new functionality. Yes, it’s true: new COBOL code is written every day. Given the long track record, it’s possible that COBOL will outlive some of the “modern” languages in use today.
While you might not find COBOL at a company like Google or Facebook, you will find it in the back-end at most banks, financial institutions, insurance companies, etc. In some ways, COBOL is the dark matter of programming languages.
What is the “attack surface” for software written in COBOL? The language itself exposes few attack avenues: a few I/O statements (ACCEPT, DISPLAY), DB access (EXEC SQL and others), data file operations (typically considered trusted sources), and program calls. Dynamic code execution, attacks directed to XML parsing, remote file inclusion, HTTP protocols and that sort of facilities that make modern, web-and-mobile-oriented languages a hacker’s paradise, do not exist in the COBOL language. Pointer support exists, but this feature is seldom used. Therefore, buffer issues common in C-like languages are infrequent in COBOL.
Let’s look at some of the security vulnerabilities that occur in COBOL, beginning with the well-known SQL injection vulnerability.
Embedded SQL (via EXEC SQL) is considered safe when using static SQL statements with interpolated host variables. A precompiler produces modified source code and a database request module, which is bound to a database package/application plan. In short, host variables are considered data, not part of the SQL code (aka “parameterized statement”), which is safe against SQL injection attacks. But dynamic SQL (PREPARE or EXECUTE IMMEDIATE) is allowed, and binding does not prevent the injection of code.
For example, the following SQL code could be injected if host variables X, Y or Z come from untrusted inputs:
* Potential SQL injection if X, Y or Z host variables come from untrusted input STRING "INSERT INTO TBL (a,b,c) VALUES (" X "," Y "," Z ")" INTO MY-SQL. EXEC SQL PREPARE STMT FROM :MY-SQL END-EXEC. EXEC SQL EXECUTE STMT END-EXEC.
In COBOL, data files are typically trusted, but injection attacks, like path traversal, are still possible. For example, MicroFocus COBOL allows a SELECT logical-file ASSIGN TO DYNAMIC data-entry. This could allow path manipulation if data-entry is partially controlled by untrusted input. CICS file commands, system functions like CLB_CREATE_DIR, and other routines are vulnerable to path manipulation attacks.
If a COBOL program will execute system commands, developers should be aware of command injection flaws. Refer to the following example:
WORKING-STORAGE SECTION. 01 prefix PIC X(10) VALUE "ls -l " 01 filename PIC X(250). 01 null-terminated-command. 05 command PIC X(256). 05 FILLER PIC X VALUE x"00". PROCEDURE DIVISION. PERFORM get-user-input PERFORM UNTIL done STRING prefix filename DELIMITED BY SIZE INTO command * FLAW: system() call with user-controlled argument CALL "SYSTEM" USING null-terminated-command RETURNING return-code-ws PERFORM get-user-input END-PERFORM.
Of course, calling unsafe C library functions from COBOL is a security problem. Never do this:
CALL 'gets' USING in-line RETURNING in-line.
COBOL does not exist in isolation. COBOL programs are called from batch jobs and online middleware. System calls and execution of CICS commands (via EXEC CICS) are often seen in COBOL code. Cryptographic libraries, like IBM ICSF or iSeries Cryptographic Services API, could be misused. Calling C library from COBOL may inherit the known issues with C and its libraries, like buffer overflow. Fortunately, this is rare.
The majority of COBOL statements (“verbs”) are relatively safe. This is because the COBOL compiler adds runtime controls that check validity for operations. For example, table subscripting or ranges in string-like character arrays are checked either by the compiler, or at least may produce an out-of-range runtime error. In either case, unintended memory areas cannot be overwritten. However, it’s possible that these checks could be disabled at compile time.
In addition, there are ways to evade such runtime checks and pre-processor safe compilation. For example, recent COBOL versions allow dynamic (“heap”) memory allocation and pointer arithmetic. Pointer arithmetic opens the same buffer-overflow issues found in the C language. Dynamic memory could be misused (the CEEFTST/CEEFRST routines under IBM System p/iSeries, CBL_ALLOC_MEM/CBL_FREE_MEM under MicroFocus Cobol, or CICS GETMAIN/FREEMAIN commands), as in the following example:
DATA DIVISION. WORKING-STORAGE SECTION. 77 AREA-POINTER USAGE IS POINTER. LINKAGE SECTION. 01 WORKAREA PIC X(100). PROCEDURE DIVISION. ... * FLAW, no FREEMAIN for the reserved main storage area workarea EXEC CICS GETMAIN SET(AREA-POINTER) LENGTH(100) END-EXEC. SET ADDRESS OF WORKAREA TO AREA-POINTER. ...
As mentioned above, the potential damage of leaking sensitive or private information could be severe. In addition to the negative impact on business operations, governments can impose massive penalties for violations of privacy regulations.
The following example shows how sensitive information may be exposed to unauthorized users. The challenge is to identify sensitive information and prevent unintended access:
WORKING-STORAGE SECTION. * This record probably holds private information 01 M001O. 05 M001O-SSN PIC X(10). 05 M001O-PERSONAL PIC X(10). 07 M001O-PERSONAL-SALARY PIC X(6). PROCEDURE DIVISION. ... EXEC CICS SEND MAP ('M001') FROM(M001O) ERASE CURSOR FREEKB END-EXEC.
A web search for the term “COBOL secure coding” returns a relatively limited set of results. But not all business languages share this situation: for example, information on ABAP secure coding is somewhat more abundant.
RPG is another legacy programming language. Like COBOL, RPG was developed in the 1960s and then later evolved by IBM into its modern incarnation (RPG IV). The RPG language is used today in IBM platforms including mainframe and mid-range systems (iSeries, AS/400 or whatever brand IBM currently uses). The “attack surface” of RPG is similar to COBOL. As with COBOL, security concerns in RPG code are not well-known.
RPG is generally free of security vulnerabilities unless the code includes dynamic SQL. Refer to the following example:
Select; When UpdByDept; WhereClause = 'Dept = ' + %EditC(DeptID:'X'); When UpdByJob; WhereClause = 'Job = ' + %EditC(Pos:'X'); When UpdByDate; WhereClause = 'HireDt < ' + %EditC(StartDate:'X'); EndSl; * FLAW, potential SQL injection Stmt = 'UPDATE EmplTable SET Sal = Sal + (Sal * ' + %Char(RaisePct) + ') WHERE ' + WhereClause; Exec SQL PREPARE DynUpdate from :Stmt; Exec SQL EXECUTE DynUpdate;
Developers may code execution of a program whose name comes from an external, untrusted input. This leads to a process control flaw (CWE 114):
F* Externally described DSPF under user control. F BATCHFILE CF P DISK F* D PROGREC DS D PROGNAME 99A I* I INTERPDSPF C OPEN INTERPDSPF C READ(E) INTERPDSPF PROGREC C* FLAW PROGNAME from external input C CALL PROGNAME
Of course, improper access to system facilities from RPG programs may lead to security vulnerabilities. In the following example, the QCMDEXC system routine is the target for a potential command injection attack (CWE 78):
F* Externally described DSPF under user control. FINTERPDSPFCF E WORKSTN USROPN F* D* Standalone field specifying the length of the command passed to QCMDEXC DCOMMANDLEN S 15P 5 INZ(80) D* C OPEN INTERPDSPF C* C* Do until the user presses <F3> C DOU *IN93 = *ON C* C* Prompt user for a CL command and read response C EXFMT COMMANDREC C* C* FLAW: Execute an OS command, potentially controlled by attacker C CALL 'QCMDEXC' C PARM COMMANDFLD C PARM COMMANDLEN
RPG allows pointer arithmetic, with the built-in “address-of” functions %ADDR / %PADDR. The following RPG code shows a potential buffer over-read flaw due to pointer arithmetic:
D Var1 S 12A D Ptr S * D Var2 S 12A BASED(Ptr) c* FLAW: Pointer arithmetic, not allowed by rule c EVAL Ptr = %ADDR(Var1) + 8192 c EVAL Var1 = 'Hello World!' c* potential bad memory deref issue c DSPLY Var2
As with COBOL, similar technical/information flow issues may arise in RPG if the developer does not follow proper security policy for preventing them. RPG and COBOL are similar with regard to how defects are injected. Each language is a vehicle that needs to be driven with care when accessing business data and system facilities.
Kiuwan helps development teams build a DevSecOps mindset and secure applications with tools for SCA and